workspace2.rs

   1// pub mod dock;
   2// pub mod item;
   3// pub mod notifications;
   4// pub mod pane;
   5// pub mod pane_group;
   6// mod persistence;
   7// pub mod searchable;
   8// pub mod shared_screen;
   9// mod status_bar;
  10// mod toolbar;
  11// mod workspace_settings;
  12
  13// use anyhow::{anyhow, Context, Result};
  14// use call::ActiveCall;
  15// use client::{
  16//     proto::{self, PeerId},
  17//     Client, Status, TypedEnvelope, UserStore,
  18// };
  19// use collections::{hash_map, HashMap, HashSet};
  20// use drag_and_drop::DragAndDrop;
  21// use futures::{
  22//     channel::{mpsc, oneshot},
  23//     future::try_join_all,
  24//     FutureExt, StreamExt,
  25// };
  26// use gpui::{
  27//     actions,
  28//     elements::*,
  29//     geometry::{
  30//         rect::RectF,
  31//         vector::{vec2f, Vector2F},
  32//     },
  33//     impl_actions,
  34//     platform::{
  35//         CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel,
  36//         WindowBounds, WindowOptions,
  37//     },
  38//     AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AnyWindowHandle, AppContext, AsyncAppContext,
  39//     Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext,
  40//     ViewHandle, WeakViewHandle, WindowContext, WindowHandle,
  41// };
  42// use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
  43// use itertools::Itertools;
  44// use language::{LanguageRegistry, Rope};
  45// use node_runtime::NodeRuntime;
  46// use std::{
  47//     any::TypeId,
  48//     borrow::Cow,
  49//     cmp, env,
  50//     future::Future,
  51//     path::{Path, PathBuf},
  52//     rc::Rc,
  53//     str,
  54//     sync::{atomic::AtomicUsize, Arc},
  55//     time::Duration,
  56// };
  57
  58// use crate::{
  59//     notifications::{simple_message_notification::MessageNotification, NotificationTracker},
  60//     persistence::model::{
  61//         DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
  62//     },
  63// };
  64// use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
  65// use lazy_static::lazy_static;
  66// use notifications::{NotificationHandle, NotifyResultExt};
  67// pub use pane::*;
  68// pub use pane_group::*;
  69// use persistence::{model::SerializedItem, DB};
  70// pub use persistence::{
  71//     model::{ItemId, WorkspaceLocation},
  72//     WorkspaceDb, DB as WORKSPACE_DB,
  73// };
  74// use postage::prelude::Stream;
  75// use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
  76// use serde::Deserialize;
  77// use shared_screen::SharedScreen;
  78// use status_bar::StatusBar;
  79// pub use status_bar::StatusItemView;
  80// use theme::{Theme, ThemeSettings};
  81// pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
  82// use util::ResultExt;
  83// pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
  84
  85// lazy_static! {
  86//     static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
  87//         .ok()
  88//         .as_deref()
  89//         .and_then(parse_pixel_position_env_var);
  90//     static ref ZED_WINDOW_POSITION: Option<Vector2F> = env::var("ZED_WINDOW_POSITION")
  91//         .ok()
  92//         .as_deref()
  93//         .and_then(parse_pixel_position_env_var);
  94// }
  95
  96// pub trait Modal: View {
  97//     fn has_focus(&self) -> bool;
  98//     fn dismiss_on_event(event: &Self::Event) -> bool;
  99// }
 100
 101// trait ModalHandle {
 102//     fn as_any(&self) -> &AnyViewHandle;
 103//     fn has_focus(&self, cx: &WindowContext) -> bool;
 104// }
 105
 106// impl<T: Modal> ModalHandle for ViewHandle<T> {
 107//     fn as_any(&self) -> &AnyViewHandle {
 108//         self
 109//     }
 110
 111//     fn has_focus(&self, cx: &WindowContext) -> bool {
 112//         self.read(cx).has_focus()
 113//     }
 114// }
 115
 116// #[derive(Clone, PartialEq)]
 117// pub struct RemoveWorktreeFromProject(pub WorktreeId);
 118
 119// actions!(
 120//     workspace,
 121//     [
 122//         Open,
 123//         NewFile,
 124//         NewWindow,
 125//         CloseWindow,
 126//         CloseInactiveTabsAndPanes,
 127//         AddFolderToProject,
 128//         Unfollow,
 129//         SaveAs,
 130//         ReloadActiveItem,
 131//         ActivatePreviousPane,
 132//         ActivateNextPane,
 133//         FollowNextCollaborator,
 134//         NewTerminal,
 135//         NewCenterTerminal,
 136//         ToggleTerminalFocus,
 137//         NewSearch,
 138//         Feedback,
 139//         Restart,
 140//         Welcome,
 141//         ToggleZoom,
 142//         ToggleLeftDock,
 143//         ToggleRightDock,
 144//         ToggleBottomDock,
 145//         CloseAllDocks,
 146//     ]
 147// );
 148
 149// #[derive(Clone, PartialEq)]
 150// pub struct OpenPaths {
 151//     pub paths: Vec<PathBuf>,
 152// }
 153
 154// #[derive(Clone, Deserialize, PartialEq)]
 155// pub struct ActivatePane(pub usize);
 156
 157// #[derive(Clone, Deserialize, PartialEq)]
 158// pub struct ActivatePaneInDirection(pub SplitDirection);
 159
 160// #[derive(Clone, Deserialize, PartialEq)]
 161// pub struct SwapPaneInDirection(pub SplitDirection);
 162
 163// #[derive(Clone, Deserialize, PartialEq)]
 164// pub struct NewFileInDirection(pub SplitDirection);
 165
 166// #[derive(Clone, PartialEq, Debug, Deserialize)]
 167// #[serde(rename_all = "camelCase")]
 168// pub struct SaveAll {
 169//     pub save_intent: Option<SaveIntent>,
 170// }
 171
 172// #[derive(Clone, PartialEq, Debug, Deserialize)]
 173// #[serde(rename_all = "camelCase")]
 174// pub struct Save {
 175//     pub save_intent: Option<SaveIntent>,
 176// }
 177
 178// #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 179// #[serde(rename_all = "camelCase")]
 180// pub struct CloseAllItemsAndPanes {
 181//     pub save_intent: Option<SaveIntent>,
 182// }
 183
 184// #[derive(Deserialize)]
 185// pub struct Toast {
 186//     id: usize,
 187//     msg: Cow<'static, str>,
 188//     #[serde(skip)]
 189//     on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
 190// }
 191
 192// impl Toast {
 193//     pub fn new<I: Into<Cow<'static, str>>>(id: usize, msg: I) -> Self {
 194//         Toast {
 195//             id,
 196//             msg: msg.into(),
 197//             on_click: None,
 198//         }
 199//     }
 200
 201//     pub fn on_click<F, M>(mut self, message: M, on_click: F) -> Self
 202//     where
 203//         M: Into<Cow<'static, str>>,
 204//         F: Fn(&mut WindowContext) + 'static,
 205//     {
 206//         self.on_click = Some((message.into(), Arc::new(on_click)));
 207//         self
 208//     }
 209// }
 210
 211// impl PartialEq for Toast {
 212//     fn eq(&self, other: &Self) -> bool {
 213//         self.id == other.id
 214//             && self.msg == other.msg
 215//             && self.on_click.is_some() == other.on_click.is_some()
 216//     }
 217// }
 218
 219// impl Clone for Toast {
 220//     fn clone(&self) -> Self {
 221//         Toast {
 222//             id: self.id,
 223//             msg: self.msg.to_owned(),
 224//             on_click: self.on_click.clone(),
 225//         }
 226//     }
 227// }
 228
 229// #[derive(Clone, Deserialize, PartialEq)]
 230// pub struct OpenTerminal {
 231//     pub working_directory: PathBuf,
 232// }
 233
 234// impl_actions!(
 235//     workspace,
 236//     [
 237//         ActivatePane,
 238//         ActivatePaneInDirection,
 239//         SwapPaneInDirection,
 240//         NewFileInDirection,
 241//         Toast,
 242//         OpenTerminal,
 243//         SaveAll,
 244//         Save,
 245//         CloseAllItemsAndPanes,
 246//     ]
 247// );
 248
 249// pub type WorkspaceId = i64;
 250
 251// pub fn init_settings(cx: &mut AppContext) {
 252//     settings::register::<WorkspaceSettings>(cx);
 253//     settings::register::<item::ItemSettings>(cx);
 254// }
 255
 256// pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
 257//     init_settings(cx);
 258//     pane::init(cx);
 259//     notifications::init(cx);
 260
 261//     cx.add_global_action({
 262//         let app_state = Arc::downgrade(&app_state);
 263//         move |_: &Open, cx: &mut AppContext| {
 264//             let mut paths = cx.prompt_for_paths(PathPromptOptions {
 265//                 files: true,
 266//                 directories: true,
 267//                 multiple: true,
 268//             });
 269
 270//             if let Some(app_state) = app_state.upgrade() {
 271//                 cx.spawn(move |mut cx| async move {
 272//                     if let Some(paths) = paths.recv().await.flatten() {
 273//                         cx.update(|cx| {
 274//                             open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
 275//                         });
 276//                     }
 277//                 })
 278//                 .detach();
 279//             }
 280//         }
 281//     });
 282//     cx.add_async_action(Workspace::open);
 283
 284//     cx.add_async_action(Workspace::follow_next_collaborator);
 285//     cx.add_async_action(Workspace::close);
 286//     cx.add_async_action(Workspace::close_inactive_items_and_panes);
 287//     cx.add_async_action(Workspace::close_all_items_and_panes);
 288//     cx.add_global_action(Workspace::close_global);
 289//     cx.add_global_action(restart);
 290//     cx.add_async_action(Workspace::save_all);
 291//     cx.add_action(Workspace::add_folder_to_project);
 292//     cx.add_action(
 293//         |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
 294//             let pane = workspace.active_pane().clone();
 295//             workspace.unfollow(&pane, cx);
 296//         },
 297//     );
 298//     cx.add_action(
 299//         |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
 300//             workspace
 301//                 .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx)
 302//                 .detach_and_log_err(cx);
 303//         },
 304//     );
 305//     cx.add_action(
 306//         |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
 307//             workspace
 308//                 .save_active_item(SaveIntent::SaveAs, cx)
 309//                 .detach_and_log_err(cx);
 310//         },
 311//     );
 312//     cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
 313//         workspace.activate_previous_pane(cx)
 314//     });
 315//     cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
 316//         workspace.activate_next_pane(cx)
 317//     });
 318
 319//     cx.add_action(
 320//         |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| {
 321//             workspace.activate_pane_in_direction(action.0, cx)
 322//         },
 323//     );
 324
 325//     cx.add_action(
 326//         |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| {
 327//             workspace.swap_pane_in_direction(action.0, cx)
 328//         },
 329//     );
 330
 331//     cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
 332//         workspace.toggle_dock(DockPosition::Left, cx);
 333//     });
 334//     cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
 335//         workspace.toggle_dock(DockPosition::Right, cx);
 336//     });
 337//     cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
 338//         workspace.toggle_dock(DockPosition::Bottom, cx);
 339//     });
 340//     cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| {
 341//         workspace.close_all_docks(cx);
 342//     });
 343//     cx.add_action(Workspace::activate_pane_at_index);
 344//     cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
 345//         workspace.reopen_closed_item(cx).detach();
 346//     });
 347//     cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| {
 348//         workspace
 349//             .go_back(workspace.active_pane().downgrade(), cx)
 350//             .detach();
 351//     });
 352//     cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| {
 353//         workspace
 354//             .go_forward(workspace.active_pane().downgrade(), cx)
 355//             .detach();
 356//     });
 357
 358//     cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
 359//         cx.spawn(|workspace, mut cx| async move {
 360//             let err = install_cli::install_cli(&cx)
 361//                 .await
 362//                 .context("Failed to create CLI symlink");
 363
 364//             workspace.update(&mut cx, |workspace, cx| {
 365//                 if matches!(err, Err(_)) {
 366//                     err.notify_err(workspace, cx);
 367//                 } else {
 368//                     workspace.show_notification(1, cx, |cx| {
 369//                         cx.add_view(|_| {
 370//                             MessageNotification::new("Successfully installed the `zed` binary")
 371//                         })
 372//                     });
 373//                 }
 374//             })
 375//         })
 376//         .detach();
 377//     });
 378// }
 379
 380// type ProjectItemBuilders = HashMap<
 381//     TypeId,
 382//     fn(ModelHandle<Project>, AnyModelHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
 383// >;
 384// pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {
 385//     cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
 386//         builders.insert(TypeId::of::<I::Item>(), |project, model, cx| {
 387//             let item = model.downcast::<I::Item>().unwrap();
 388//             Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx)))
 389//         });
 390//     });
 391// }
 392
 393// type FollowableItemBuilder = fn(
 394//     ViewHandle<Pane>,
 395//     ViewHandle<Workspace>,
 396//     ViewId,
 397//     &mut Option<proto::view::Variant>,
 398//     &mut AppContext,
 399// ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>;
 400// type FollowableItemBuilders = HashMap<
 401//     TypeId,
 402//     (
 403//         FollowableItemBuilder,
 404//         fn(&AnyViewHandle) -> Box<dyn FollowableItemHandle>,
 405//     ),
 406// >;
 407// pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) {
 408//     cx.update_default_global(|builders: &mut FollowableItemBuilders, _| {
 409//         builders.insert(
 410//             TypeId::of::<I>(),
 411//             (
 412//                 |pane, workspace, id, state, cx| {
 413//                     I::from_state_proto(pane, workspace, id, state, cx).map(|task| {
 414//                         cx.foreground()
 415//                             .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
 416//                     })
 417//                 },
 418//                 |this| Box::new(this.clone().downcast::<I>().unwrap()),
 419//             ),
 420//         );
 421//     });
 422// }
 423
 424// type ItemDeserializers = HashMap<
 425//     Arc<str>,
 426//     fn(
 427//         ModelHandle<Project>,
 428//         WeakViewHandle<Workspace>,
 429//         WorkspaceId,
 430//         ItemId,
 431//         &mut ViewContext<Pane>,
 432//     ) -> Task<Result<Box<dyn ItemHandle>>>,
 433// >;
 434// pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
 435//     cx.update_default_global(|deserializers: &mut ItemDeserializers, _cx| {
 436//         if let Some(serialized_item_kind) = I::serialized_item_kind() {
 437//             deserializers.insert(
 438//                 Arc::from(serialized_item_kind),
 439//                 |project, workspace, workspace_id, item_id, cx| {
 440//                     let task = I::deserialize(project, workspace, workspace_id, item_id, cx);
 441//                     cx.foreground()
 442//                         .spawn(async { Ok(Box::new(task.await?) as Box<_>) })
 443//                 },
 444//             );
 445//         }
 446//     });
 447// }
 448
 449// pub struct AppState {
 450//     pub languages: Arc<LanguageRegistry>,
 451//     pub client: Arc<Client>,
 452//     pub user_store: ModelHandle<UserStore>,
 453//     pub workspace_store: ModelHandle<WorkspaceStore>,
 454//     pub fs: Arc<dyn fs::Fs>,
 455//     pub build_window_options:
 456//         fn(Option<WindowBounds>, Option<uuid::Uuid>, &dyn Platform) -> WindowOptions<'static>,
 457//     pub initialize_workspace:
 458//         fn(WeakViewHandle<Workspace>, bool, Arc<AppState>, AsyncAppContext) -> Task<Result<()>>,
 459//     pub background_actions: BackgroundActions,
 460//     pub node_runtime: Arc<dyn NodeRuntime>,
 461// }
 462
 463// pub struct WorkspaceStore {
 464//     workspaces: HashSet<WeakViewHandle<Workspace>>,
 465//     followers: Vec<Follower>,
 466//     client: Arc<Client>,
 467//     _subscriptions: Vec<client::Subscription>,
 468// }
 469
 470// #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
 471// struct Follower {
 472//     project_id: Option<u64>,
 473//     peer_id: PeerId,
 474// }
 475
 476// impl AppState {
 477//     #[cfg(any(test, feature = "test-support"))]
 478//     pub fn test(cx: &mut AppContext) -> Arc<Self> {
 479//         use node_runtime::FakeNodeRuntime;
 480//         use settings::SettingsStore;
 481
 482//         if !cx.has_global::<SettingsStore>() {
 483//             cx.set_global(SettingsStore::test(cx));
 484//         }
 485
 486//         let fs = fs::FakeFs::new(cx.background().clone());
 487//         let languages = Arc::new(LanguageRegistry::test());
 488//         let http_client = util::http::FakeHttpClient::with_404_response();
 489//         let client = Client::new(http_client.clone(), cx);
 490//         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
 491//         let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
 492
 493//         theme::init((), cx);
 494//         client::init(&client, cx);
 495//         crate::init_settings(cx);
 496
 497//         Arc::new(Self {
 498//             client,
 499//             fs,
 500//             languages,
 501//             user_store,
 502//             // channel_store,
 503//             workspace_store,
 504//             node_runtime: FakeNodeRuntime::new(),
 505//             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
 506//             build_window_options: |_, _, _| Default::default(),
 507//             background_actions: || &[],
 508//         })
 509//     }
 510// }
 511
 512// struct DelayedDebouncedEditAction {
 513//     task: Option<Task<()>>,
 514//     cancel_channel: Option<oneshot::Sender<()>>,
 515// }
 516
 517// impl DelayedDebouncedEditAction {
 518//     fn new() -> DelayedDebouncedEditAction {
 519//         DelayedDebouncedEditAction {
 520//             task: None,
 521//             cancel_channel: None,
 522//         }
 523//     }
 524
 525//     fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Workspace>, func: F)
 526//     where
 527//         F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
 528//     {
 529//         if let Some(channel) = self.cancel_channel.take() {
 530//             _ = channel.send(());
 531//         }
 532
 533//         let (sender, mut receiver) = oneshot::channel::<()>();
 534//         self.cancel_channel = Some(sender);
 535
 536//         let previous_task = self.task.take();
 537//         self.task = Some(cx.spawn(|workspace, mut cx| async move {
 538//             let mut timer = cx.background().timer(delay).fuse();
 539//             if let Some(previous_task) = previous_task {
 540//                 previous_task.await;
 541//             }
 542
 543//             futures::select_biased! {
 544//                 _ = receiver => return,
 545//                     _ = timer => {}
 546//             }
 547
 548//             if let Some(result) = workspace
 549//                 .update(&mut cx, |workspace, cx| (func)(workspace, cx))
 550//                 .log_err()
 551//             {
 552//                 result.await.log_err();
 553//             }
 554//         }));
 555//     }
 556// }
 557
 558// pub enum Event {
 559//     PaneAdded(ViewHandle<Pane>),
 560//     ContactRequestedJoin(u64),
 561// }
 562
 563// pub struct Workspace {
 564//     weak_self: WeakViewHandle<Self>,
 565//     modal: Option<ActiveModal>,
 566//     zoomed: Option<AnyWeakViewHandle>,
 567//     zoomed_position: Option<DockPosition>,
 568//     center: PaneGroup,
 569//     left_dock: ViewHandle<Dock>,
 570//     bottom_dock: ViewHandle<Dock>,
 571//     right_dock: ViewHandle<Dock>,
 572//     panes: Vec<ViewHandle<Pane>>,
 573//     panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
 574//     active_pane: ViewHandle<Pane>,
 575//     last_active_center_pane: Option<WeakViewHandle<Pane>>,
 576//     last_active_view_id: Option<proto::ViewId>,
 577//     status_bar: ViewHandle<StatusBar>,
 578//     titlebar_item: Option<AnyViewHandle>,
 579//     notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
 580//     project: ModelHandle<Project>,
 581//     follower_states: HashMap<ViewHandle<Pane>, FollowerState>,
 582//     last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
 583//     window_edited: bool,
 584//     active_call: Option<(ModelHandle<ActiveCall>, Vec<Subscription>)>,
 585//     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
 586//     database_id: WorkspaceId,
 587//     app_state: Arc<AppState>,
 588//     subscriptions: Vec<Subscription>,
 589//     _apply_leader_updates: Task<Result<()>>,
 590//     _observe_current_user: Task<Result<()>>,
 591//     _schedule_serialize: Option<Task<()>>,
 592//     pane_history_timestamp: Arc<AtomicUsize>,
 593// }
 594
 595// struct ActiveModal {
 596//     view: Box<dyn ModalHandle>,
 597//     previously_focused_view_id: Option<usize>,
 598// }
 599
 600// #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 601// pub struct ViewId {
 602//     pub creator: PeerId,
 603//     pub id: u64,
 604// }
 605
 606// #[derive(Default)]
 607// struct FollowerState {
 608//     leader_id: PeerId,
 609//     active_view_id: Option<ViewId>,
 610//     items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
 611// }
 612
 613// enum WorkspaceBounds {}
 614
 615// impl Workspace {
 616//     pub fn new(
 617//         workspace_id: WorkspaceId,
 618//         project: ModelHandle<Project>,
 619//         app_state: Arc<AppState>,
 620//         cx: &mut ViewContext<Self>,
 621//     ) -> Self {
 622//         cx.observe(&project, |_, _, cx| cx.notify()).detach();
 623//         cx.subscribe(&project, move |this, _, event, cx| {
 624//             match event {
 625//                 project::Event::RemoteIdChanged(_) => {
 626//                     this.update_window_title(cx);
 627//                 }
 628
 629//                 project::Event::CollaboratorLeft(peer_id) => {
 630//                     this.collaborator_left(*peer_id, cx);
 631//                 }
 632
 633//                 project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded => {
 634//                     this.update_window_title(cx);
 635//                     this.serialize_workspace(cx);
 636//                 }
 637
 638//                 project::Event::DisconnectedFromHost => {
 639//                     this.update_window_edited(cx);
 640//                     cx.blur();
 641//                 }
 642
 643//                 project::Event::Closed => {
 644//                     cx.remove_window();
 645//                 }
 646
 647//                 project::Event::DeletedEntry(entry_id) => {
 648//                     for pane in this.panes.iter() {
 649//                         pane.update(cx, |pane, cx| {
 650//                             pane.handle_deleted_project_item(*entry_id, cx)
 651//                         });
 652//                     }
 653//                 }
 654
 655//                 project::Event::Notification(message) => this.show_notification(0, cx, |cx| {
 656//                     cx.add_view(|_| MessageNotification::new(message.clone()))
 657//                 }),
 658
 659//                 _ => {}
 660//             }
 661//             cx.notify()
 662//         })
 663//         .detach();
 664
 665//         let weak_handle = cx.weak_handle();
 666//         let pane_history_timestamp = Arc::new(AtomicUsize::new(0));
 667
 668//         let center_pane = cx.add_view(|cx| {
 669//             Pane::new(
 670//                 weak_handle.clone(),
 671//                 project.clone(),
 672//                 app_state.background_actions,
 673//                 pane_history_timestamp.clone(),
 674//                 cx,
 675//             )
 676//         });
 677//         cx.subscribe(&center_pane, Self::handle_pane_event).detach();
 678//         cx.focus(&center_pane);
 679//         cx.emit(Event::PaneAdded(center_pane.clone()));
 680
 681//         app_state.workspace_store.update(cx, |store, _| {
 682//             store.workspaces.insert(weak_handle.clone());
 683//         });
 684
 685//         let mut current_user = app_state.user_store.read(cx).watch_current_user();
 686//         let mut connection_status = app_state.client.status();
 687//         let _observe_current_user = cx.spawn(|this, mut cx| async move {
 688//             current_user.recv().await;
 689//             connection_status.recv().await;
 690//             let mut stream =
 691//                 Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
 692
 693//             while stream.recv().await.is_some() {
 694//                 this.update(&mut cx, |_, cx| cx.notify())?;
 695//             }
 696//             anyhow::Ok(())
 697//         });
 698
 699//         // All leader updates are enqueued and then processed in a single task, so
 700//         // that each asynchronous operation can be run in order.
 701//         let (leader_updates_tx, mut leader_updates_rx) =
 702//             mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>();
 703//         let _apply_leader_updates = cx.spawn(|this, mut cx| async move {
 704//             while let Some((leader_id, update)) = leader_updates_rx.next().await {
 705//                 Self::process_leader_update(&this, leader_id, update, &mut cx)
 706//                     .await
 707//                     .log_err();
 708//             }
 709
 710//             Ok(())
 711//         });
 712
 713//         cx.emit_global(WorkspaceCreated(weak_handle.clone()));
 714
 715//         let left_dock = cx.add_view(|_| Dock::new(DockPosition::Left));
 716//         let bottom_dock = cx.add_view(|_| Dock::new(DockPosition::Bottom));
 717//         let right_dock = cx.add_view(|_| Dock::new(DockPosition::Right));
 718//         let left_dock_buttons =
 719//             cx.add_view(|cx| PanelButtons::new(left_dock.clone(), weak_handle.clone(), cx));
 720//         let bottom_dock_buttons =
 721//             cx.add_view(|cx| PanelButtons::new(bottom_dock.clone(), weak_handle.clone(), cx));
 722//         let right_dock_buttons =
 723//             cx.add_view(|cx| PanelButtons::new(right_dock.clone(), weak_handle.clone(), cx));
 724//         let status_bar = cx.add_view(|cx| {
 725//             let mut status_bar = StatusBar::new(&center_pane.clone(), cx);
 726//             status_bar.add_left_item(left_dock_buttons, cx);
 727//             status_bar.add_right_item(right_dock_buttons, cx);
 728//             status_bar.add_right_item(bottom_dock_buttons, cx);
 729//             status_bar
 730//         });
 731
 732//         cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
 733//             drag_and_drop.register_container(weak_handle.clone());
 734//         });
 735
 736//         let mut active_call = None;
 737//         if cx.has_global::<ModelHandle<ActiveCall>>() {
 738//             let call = cx.global::<ModelHandle<ActiveCall>>().clone();
 739//             let mut subscriptions = Vec::new();
 740//             subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
 741//             active_call = Some((call, subscriptions));
 742//         }
 743
 744//         let subscriptions = vec![
 745//             cx.observe_fullscreen(|_, _, cx| cx.notify()),
 746//             cx.observe_window_activation(Self::on_window_activation_changed),
 747//             cx.observe_window_bounds(move |_, mut bounds, display, cx| {
 748//                 // Transform fixed bounds to be stored in terms of the containing display
 749//                 if let WindowBounds::Fixed(mut window_bounds) = bounds {
 750//                     if let Some(screen) = cx.platform().screen_by_id(display) {
 751//                         let screen_bounds = screen.bounds();
 752//                         window_bounds
 753//                             .set_origin_x(window_bounds.origin_x() - screen_bounds.origin_x());
 754//                         window_bounds
 755//                             .set_origin_y(window_bounds.origin_y() - screen_bounds.origin_y());
 756//                         bounds = WindowBounds::Fixed(window_bounds);
 757//                     }
 758//                 }
 759
 760//                 cx.background()
 761//                     .spawn(DB.set_window_bounds(workspace_id, bounds, display))
 762//                     .detach_and_log_err(cx);
 763//             }),
 764//             cx.observe(&left_dock, |this, _, cx| {
 765//                 this.serialize_workspace(cx);
 766//                 cx.notify();
 767//             }),
 768//             cx.observe(&bottom_dock, |this, _, cx| {
 769//                 this.serialize_workspace(cx);
 770//                 cx.notify();
 771//             }),
 772//             cx.observe(&right_dock, |this, _, cx| {
 773//                 this.serialize_workspace(cx);
 774//                 cx.notify();
 775//             }),
 776//         ];
 777
 778//         cx.defer(|this, cx| this.update_window_title(cx));
 779//         Workspace {
 780//             weak_self: weak_handle.clone(),
 781//             modal: None,
 782//             zoomed: None,
 783//             zoomed_position: None,
 784//             center: PaneGroup::new(center_pane.clone()),
 785//             panes: vec![center_pane.clone()],
 786//             panes_by_item: Default::default(),
 787//             active_pane: center_pane.clone(),
 788//             last_active_center_pane: Some(center_pane.downgrade()),
 789//             last_active_view_id: None,
 790//             status_bar,
 791//             titlebar_item: None,
 792//             notifications: Default::default(),
 793//             left_dock,
 794//             bottom_dock,
 795//             right_dock,
 796//             project: project.clone(),
 797//             follower_states: Default::default(),
 798//             last_leaders_by_pane: Default::default(),
 799//             window_edited: false,
 800//             active_call,
 801//             database_id: workspace_id,
 802//             app_state,
 803//             _observe_current_user,
 804//             _apply_leader_updates,
 805//             _schedule_serialize: None,
 806//             leader_updates_tx,
 807//             subscriptions,
 808//             pane_history_timestamp,
 809//         }
 810//     }
 811
 812//     fn new_local(
 813//         abs_paths: Vec<PathBuf>,
 814//         app_state: Arc<AppState>,
 815//         requesting_window: Option<WindowHandle<Workspace>>,
 816//         cx: &mut AppContext,
 817//     ) -> Task<(
 818//         WeakViewHandle<Workspace>,
 819//         Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
 820//     )> {
 821//         let project_handle = Project::local(
 822//             app_state.client.clone(),
 823//             app_state.node_runtime.clone(),
 824//             app_state.user_store.clone(),
 825//             app_state.languages.clone(),
 826//             app_state.fs.clone(),
 827//             cx,
 828//         );
 829
 830//         cx.spawn(|mut cx| async move {
 831//             let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice());
 832
 833//             let paths_to_open = Arc::new(abs_paths);
 834
 835//             // Get project paths for all of the abs_paths
 836//             let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
 837//             let mut project_paths: Vec<(PathBuf, Option<ProjectPath>)> =
 838//                 Vec::with_capacity(paths_to_open.len());
 839//             for path in paths_to_open.iter().cloned() {
 840//                 if let Some((worktree, project_entry)) = cx
 841//                     .update(|cx| {
 842//                         Workspace::project_path_for_path(project_handle.clone(), &path, true, cx)
 843//                     })
 844//                     .await
 845//                     .log_err()
 846//                 {
 847//                     worktree_roots.insert(worktree.read_with(&mut cx, |tree, _| tree.abs_path()));
 848//                     project_paths.push((path, Some(project_entry)));
 849//                 } else {
 850//                     project_paths.push((path, None));
 851//                 }
 852//             }
 853
 854//             let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() {
 855//                 serialized_workspace.id
 856//             } else {
 857//                 DB.next_id().await.unwrap_or(0)
 858//             };
 859
 860//             let window = if let Some(window) = requesting_window {
 861//                 window.replace_root(&mut cx, |cx| {
 862//                     Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
 863//                 });
 864//                 window
 865//             } else {
 866//                 {
 867//                     let window_bounds_override = window_bounds_env_override(&cx);
 868//                     let (bounds, display) = if let Some(bounds) = window_bounds_override {
 869//                         (Some(bounds), None)
 870//                     } else {
 871//                         serialized_workspace
 872//                             .as_ref()
 873//                             .and_then(|serialized_workspace| {
 874//                                 let display = serialized_workspace.display?;
 875//                                 let mut bounds = serialized_workspace.bounds?;
 876
 877//                                 // Stored bounds are relative to the containing display.
 878//                                 // So convert back to global coordinates if that screen still exists
 879//                                 if let WindowBounds::Fixed(mut window_bounds) = bounds {
 880//                                     if let Some(screen) = cx.platform().screen_by_id(display) {
 881//                                         let screen_bounds = screen.bounds();
 882//                                         window_bounds.set_origin_x(
 883//                                             window_bounds.origin_x() + screen_bounds.origin_x(),
 884//                                         );
 885//                                         window_bounds.set_origin_y(
 886//                                             window_bounds.origin_y() + screen_bounds.origin_y(),
 887//                                         );
 888//                                         bounds = WindowBounds::Fixed(window_bounds);
 889//                                     } else {
 890//                                         // Screen no longer exists. Return none here.
 891//                                         return None;
 892//                                     }
 893//                                 }
 894
 895//                                 Some((bounds, display))
 896//                             })
 897//                             .unzip()
 898//                     };
 899
 900//                     // Use the serialized workspace to construct the new window
 901//                     cx.add_window(
 902//                         (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
 903//                         |cx| {
 904//                             Workspace::new(
 905//                                 workspace_id,
 906//                                 project_handle.clone(),
 907//                                 app_state.clone(),
 908//                                 cx,
 909//                             )
 910//                         },
 911//                     )
 912//                 }
 913//             };
 914
 915//             // We haven't yielded the main thread since obtaining the window handle,
 916//             // so the window exists.
 917//             let workspace = window.root(&cx).unwrap();
 918
 919//             (app_state.initialize_workspace)(
 920//                 workspace.downgrade(),
 921//                 serialized_workspace.is_some(),
 922//                 app_state.clone(),
 923//                 cx.clone(),
 924//             )
 925//             .await
 926//             .log_err();
 927
 928//             window.update(&mut cx, |cx| cx.activate_window());
 929
 930//             let workspace = workspace.downgrade();
 931//             notify_if_database_failed(&workspace, &mut cx);
 932//             let opened_items = open_items(
 933//                 serialized_workspace,
 934//                 &workspace,
 935//                 project_paths,
 936//                 app_state,
 937//                 cx,
 938//             )
 939//             .await
 940//             .unwrap_or_default();
 941
 942//             (workspace, opened_items)
 943//         })
 944//     }
 945
 946//     pub fn weak_handle(&self) -> WeakViewHandle<Self> {
 947//         self.weak_self.clone()
 948//     }
 949
 950//     pub fn left_dock(&self) -> &ViewHandle<Dock> {
 951//         &self.left_dock
 952//     }
 953
 954//     pub fn bottom_dock(&self) -> &ViewHandle<Dock> {
 955//         &self.bottom_dock
 956//     }
 957
 958//     pub fn right_dock(&self) -> &ViewHandle<Dock> {
 959//         &self.right_dock
 960//     }
 961
 962//     pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
 963//     where
 964//         T::Event: std::fmt::Debug,
 965//     {
 966//         self.add_panel_with_extra_event_handler(panel, cx, |_, _, _, _| {})
 967//     }
 968
 969//     pub fn add_panel_with_extra_event_handler<T: Panel, F>(
 970//         &mut self,
 971//         panel: ViewHandle<T>,
 972//         cx: &mut ViewContext<Self>,
 973//         handler: F,
 974//     ) where
 975//         T::Event: std::fmt::Debug,
 976//         F: Fn(&mut Self, &ViewHandle<T>, &T::Event, &mut ViewContext<Self>) + 'static,
 977//     {
 978//         let dock = match panel.position(cx) {
 979//             DockPosition::Left => &self.left_dock,
 980//             DockPosition::Bottom => &self.bottom_dock,
 981//             DockPosition::Right => &self.right_dock,
 982//         };
 983
 984//         self.subscriptions.push(cx.subscribe(&panel, {
 985//             let mut dock = dock.clone();
 986//             let mut prev_position = panel.position(cx);
 987//             move |this, panel, event, cx| {
 988//                 if T::should_change_position_on_event(event) {
 989//                     let new_position = panel.read(cx).position(cx);
 990//                     let mut was_visible = false;
 991//                     dock.update(cx, |dock, cx| {
 992//                         prev_position = new_position;
 993
 994//                         was_visible = dock.is_open()
 995//                             && dock
 996//                                 .visible_panel()
 997//                                 .map_or(false, |active_panel| active_panel.id() == panel.id());
 998//                         dock.remove_panel(&panel, cx);
 999//                     });
1000
1001//                     if panel.is_zoomed(cx) {
1002//                         this.zoomed_position = Some(new_position);
1003//                     }
1004
1005//                     dock = match panel.read(cx).position(cx) {
1006//                         DockPosition::Left => &this.left_dock,
1007//                         DockPosition::Bottom => &this.bottom_dock,
1008//                         DockPosition::Right => &this.right_dock,
1009//                     }
1010//                     .clone();
1011//                     dock.update(cx, |dock, cx| {
1012//                         dock.add_panel(panel.clone(), cx);
1013//                         if was_visible {
1014//                             dock.set_open(true, cx);
1015//                             dock.activate_panel(dock.panels_len() - 1, cx);
1016//                         }
1017//                     });
1018//                 } else if T::should_zoom_in_on_event(event) {
1019//                     dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
1020//                     if !panel.has_focus(cx) {
1021//                         cx.focus(&panel);
1022//                     }
1023//                     this.zoomed = Some(panel.downgrade().into_any());
1024//                     this.zoomed_position = Some(panel.read(cx).position(cx));
1025//                 } else if T::should_zoom_out_on_event(event) {
1026//                     dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
1027//                     if this.zoomed_position == Some(prev_position) {
1028//                         this.zoomed = None;
1029//                         this.zoomed_position = None;
1030//                     }
1031//                     cx.notify();
1032//                 } else if T::is_focus_event(event) {
1033//                     let position = panel.read(cx).position(cx);
1034//                     this.dismiss_zoomed_items_to_reveal(Some(position), cx);
1035//                     if panel.is_zoomed(cx) {
1036//                         this.zoomed = Some(panel.downgrade().into_any());
1037//                         this.zoomed_position = Some(position);
1038//                     } else {
1039//                         this.zoomed = None;
1040//                         this.zoomed_position = None;
1041//                     }
1042//                     this.update_active_view_for_followers(cx);
1043//                     cx.notify();
1044//                 } else {
1045//                     handler(this, &panel, event, cx)
1046//                 }
1047//             }
1048//         }));
1049
1050//         dock.update(cx, |dock, cx| dock.add_panel(panel, cx));
1051//     }
1052
1053//     pub fn status_bar(&self) -> &ViewHandle<StatusBar> {
1054//         &self.status_bar
1055//     }
1056
1057//     pub fn app_state(&self) -> &Arc<AppState> {
1058//         &self.app_state
1059//     }
1060
1061//     pub fn user_store(&self) -> &ModelHandle<UserStore> {
1062//         &self.app_state.user_store
1063//     }
1064
1065//     pub fn project(&self) -> &ModelHandle<Project> {
1066//         &self.project
1067//     }
1068
1069//     pub fn recent_navigation_history(
1070//         &self,
1071//         limit: Option<usize>,
1072//         cx: &AppContext,
1073//     ) -> Vec<(ProjectPath, Option<PathBuf>)> {
1074//         let mut abs_paths_opened: HashMap<PathBuf, HashSet<ProjectPath>> = HashMap::default();
1075//         let mut history: HashMap<ProjectPath, (Option<PathBuf>, usize)> = HashMap::default();
1076//         for pane in &self.panes {
1077//             let pane = pane.read(cx);
1078//             pane.nav_history()
1079//                 .for_each_entry(cx, |entry, (project_path, fs_path)| {
1080//                     if let Some(fs_path) = &fs_path {
1081//                         abs_paths_opened
1082//                             .entry(fs_path.clone())
1083//                             .or_default()
1084//                             .insert(project_path.clone());
1085//                     }
1086//                     let timestamp = entry.timestamp;
1087//                     match history.entry(project_path) {
1088//                         hash_map::Entry::Occupied(mut entry) => {
1089//                             let (_, old_timestamp) = entry.get();
1090//                             if &timestamp > old_timestamp {
1091//                                 entry.insert((fs_path, timestamp));
1092//                             }
1093//                         }
1094//                         hash_map::Entry::Vacant(entry) => {
1095//                             entry.insert((fs_path, timestamp));
1096//                         }
1097//                     }
1098//                 });
1099//         }
1100
1101//         history
1102//             .into_iter()
1103//             .sorted_by_key(|(_, (_, timestamp))| *timestamp)
1104//             .map(|(project_path, (fs_path, _))| (project_path, fs_path))
1105//             .rev()
1106//             .filter(|(history_path, abs_path)| {
1107//                 let latest_project_path_opened = abs_path
1108//                     .as_ref()
1109//                     .and_then(|abs_path| abs_paths_opened.get(abs_path))
1110//                     .and_then(|project_paths| {
1111//                         project_paths
1112//                             .iter()
1113//                             .max_by(|b1, b2| b1.worktree_id.cmp(&b2.worktree_id))
1114//                     });
1115
1116//                 match latest_project_path_opened {
1117//                     Some(latest_project_path_opened) => latest_project_path_opened == history_path,
1118//                     None => true,
1119//                 }
1120//             })
1121//             .take(limit.unwrap_or(usize::MAX))
1122//             .collect()
1123//     }
1124
1125//     fn navigate_history(
1126//         &mut self,
1127//         pane: WeakViewHandle<Pane>,
1128//         mode: NavigationMode,
1129//         cx: &mut ViewContext<Workspace>,
1130//     ) -> Task<Result<()>> {
1131//         let to_load = if let Some(pane) = pane.upgrade(cx) {
1132//             cx.focus(&pane);
1133
1134//             pane.update(cx, |pane, cx| {
1135//                 loop {
1136//                     // Retrieve the weak item handle from the history.
1137//                     let entry = pane.nav_history_mut().pop(mode, cx)?;
1138
1139//                     // If the item is still present in this pane, then activate it.
1140//                     if let Some(index) = entry
1141//                         .item
1142//                         .upgrade(cx)
1143//                         .and_then(|v| pane.index_for_item(v.as_ref()))
1144//                     {
1145//                         let prev_active_item_index = pane.active_item_index();
1146//                         pane.nav_history_mut().set_mode(mode);
1147//                         pane.activate_item(index, true, true, cx);
1148//                         pane.nav_history_mut().set_mode(NavigationMode::Normal);
1149
1150//                         let mut navigated = prev_active_item_index != pane.active_item_index();
1151//                         if let Some(data) = entry.data {
1152//                             navigated |= pane.active_item()?.navigate(data, cx);
1153//                         }
1154
1155//                         if navigated {
1156//                             break None;
1157//                         }
1158//                     }
1159//                     // If the item is no longer present in this pane, then retrieve its
1160//                     // project path in order to reopen it.
1161//                     else {
1162//                         break pane
1163//                             .nav_history()
1164//                             .path_for_item(entry.item.id())
1165//                             .map(|(project_path, _)| (project_path, entry));
1166//                     }
1167//                 }
1168//             })
1169//         } else {
1170//             None
1171//         };
1172
1173//         if let Some((project_path, entry)) = to_load {
1174//             // If the item was no longer present, then load it again from its previous path.
1175//             let task = self.load_path(project_path, cx);
1176//             cx.spawn(|workspace, mut cx| async move {
1177//                 let task = task.await;
1178//                 let mut navigated = false;
1179//                 if let Some((project_entry_id, build_item)) = task.log_err() {
1180//                     let prev_active_item_id = pane.update(&mut cx, |pane, _| {
1181//                         pane.nav_history_mut().set_mode(mode);
1182//                         pane.active_item().map(|p| p.id())
1183//                     })?;
1184
1185//                     pane.update(&mut cx, |pane, cx| {
1186//                         let item = pane.open_item(project_entry_id, true, cx, build_item);
1187//                         navigated |= Some(item.id()) != prev_active_item_id;
1188//                         pane.nav_history_mut().set_mode(NavigationMode::Normal);
1189//                         if let Some(data) = entry.data {
1190//                             navigated |= item.navigate(data, cx);
1191//                         }
1192//                     })?;
1193//                 }
1194
1195//                 if !navigated {
1196//                     workspace
1197//                         .update(&mut cx, |workspace, cx| {
1198//                             Self::navigate_history(workspace, pane, mode, cx)
1199//                         })?
1200//                         .await?;
1201//                 }
1202
1203//                 Ok(())
1204//             })
1205//         } else {
1206//             Task::ready(Ok(()))
1207//         }
1208//     }
1209
1210//     pub fn go_back(
1211//         &mut self,
1212//         pane: WeakViewHandle<Pane>,
1213//         cx: &mut ViewContext<Workspace>,
1214//     ) -> Task<Result<()>> {
1215//         self.navigate_history(pane, NavigationMode::GoingBack, cx)
1216//     }
1217
1218//     pub fn go_forward(
1219//         &mut self,
1220//         pane: WeakViewHandle<Pane>,
1221//         cx: &mut ViewContext<Workspace>,
1222//     ) -> Task<Result<()>> {
1223//         self.navigate_history(pane, NavigationMode::GoingForward, cx)
1224//     }
1225
1226//     pub fn reopen_closed_item(&mut self, cx: &mut ViewContext<Workspace>) -> Task<Result<()>> {
1227//         self.navigate_history(
1228//             self.active_pane().downgrade(),
1229//             NavigationMode::ReopeningClosedItem,
1230//             cx,
1231//         )
1232//     }
1233
1234//     pub fn client(&self) -> &Client {
1235//         &self.app_state.client
1236//     }
1237
1238//     pub fn set_titlebar_item(&mut self, item: AnyViewHandle, cx: &mut ViewContext<Self>) {
1239//         self.titlebar_item = Some(item);
1240//         cx.notify();
1241//     }
1242
1243//     pub fn titlebar_item(&self) -> Option<AnyViewHandle> {
1244//         self.titlebar_item.clone()
1245//     }
1246
1247//     /// Call the given callback with a workspace whose project is local.
1248//     ///
1249//     /// If the given workspace has a local project, then it will be passed
1250//     /// to the callback. Otherwise, a new empty window will be created.
1251//     pub fn with_local_workspace<T, F>(
1252//         &mut self,
1253//         cx: &mut ViewContext<Self>,
1254//         callback: F,
1255//     ) -> Task<Result<T>>
1256//     where
1257//         T: 'static,
1258//         F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
1259//     {
1260//         if self.project.read(cx).is_local() {
1261//             Task::Ready(Some(Ok(callback(self, cx))))
1262//         } else {
1263//             let task = Self::new_local(Vec::new(), self.app_state.clone(), None, cx);
1264//             cx.spawn(|_vh, mut cx| async move {
1265//                 let (workspace, _) = task.await;
1266//                 workspace.update(&mut cx, callback)
1267//             })
1268//         }
1269//     }
1270
1271//     pub fn worktrees<'a>(
1272//         &self,
1273//         cx: &'a AppContext,
1274//     ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
1275//         self.project.read(cx).worktrees(cx)
1276//     }
1277
1278//     pub fn visible_worktrees<'a>(
1279//         &self,
1280//         cx: &'a AppContext,
1281//     ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
1282//         self.project.read(cx).visible_worktrees(cx)
1283//     }
1284
1285//     pub fn worktree_scans_complete(&self, cx: &AppContext) -> impl Future<Output = ()> + 'static {
1286//         let futures = self
1287//             .worktrees(cx)
1288//             .filter_map(|worktree| worktree.read(cx).as_local())
1289//             .map(|worktree| worktree.scan_complete())
1290//             .collect::<Vec<_>>();
1291//         async move {
1292//             for future in futures {
1293//                 future.await;
1294//             }
1295//         }
1296//     }
1297
1298//     pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
1299//         cx.spawn(|mut cx| async move {
1300//             let window = cx
1301//                 .windows()
1302//                 .into_iter()
1303//                 .find(|window| window.is_active(&cx).unwrap_or(false));
1304//             if let Some(window) = window {
1305//                 //This can only get called when the window's project connection has been lost
1306//                 //so we don't need to prompt the user for anything and instead just close the window
1307//                 window.remove(&mut cx);
1308//             }
1309//         })
1310//         .detach();
1311//     }
1312
1313//     pub fn close(
1314//         &mut self,
1315//         _: &CloseWindow,
1316//         cx: &mut ViewContext<Self>,
1317//     ) -> Option<Task<Result<()>>> {
1318//         let window = cx.window();
1319//         let prepare = self.prepare_to_close(false, cx);
1320//         Some(cx.spawn(|_, mut cx| async move {
1321//             if prepare.await? {
1322//                 window.remove(&mut cx);
1323//             }
1324//             Ok(())
1325//         }))
1326//     }
1327
1328//     pub fn prepare_to_close(
1329//         &mut self,
1330//         quitting: bool,
1331//         cx: &mut ViewContext<Self>,
1332//     ) -> Task<Result<bool>> {
1333//         let active_call = self.active_call().cloned();
1334//         let window = cx.window();
1335
1336//         cx.spawn(|this, mut cx| async move {
1337//             let workspace_count = cx
1338//                 .windows()
1339//                 .into_iter()
1340//                 .filter(|window| window.root_is::<Workspace>())
1341//                 .count();
1342
1343//             if let Some(active_call) = active_call {
1344//                 if !quitting
1345//                     && workspace_count == 1
1346//                     && active_call.read_with(&cx, |call, _| call.room().is_some())
1347//                 {
1348//                     let answer = window.prompt(
1349//                         PromptLevel::Warning,
1350//                         "Do you want to leave the current call?",
1351//                         &["Close window and hang up", "Cancel"],
1352//                         &mut cx,
1353//                     );
1354
1355//                     if let Some(mut answer) = answer {
1356//                         if answer.next().await == Some(1) {
1357//                             return anyhow::Ok(false);
1358//                         } else {
1359//                             active_call
1360//                                 .update(&mut cx, |call, cx| call.hang_up(cx))
1361//                                 .await
1362//                                 .log_err();
1363//                         }
1364//                     }
1365//                 }
1366//             }
1367
1368//             Ok(this
1369//                 .update(&mut cx, |this, cx| {
1370//                     this.save_all_internal(SaveIntent::Close, cx)
1371//                 })?
1372//                 .await?)
1373//         })
1374//     }
1375
1376//     fn save_all(
1377//         &mut self,
1378//         action: &SaveAll,
1379//         cx: &mut ViewContext<Self>,
1380//     ) -> Option<Task<Result<()>>> {
1381//         let save_all =
1382//             self.save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx);
1383//         Some(cx.foreground().spawn(async move {
1384//             save_all.await?;
1385//             Ok(())
1386//         }))
1387//     }
1388
1389//     fn save_all_internal(
1390//         &mut self,
1391//         mut save_intent: SaveIntent,
1392//         cx: &mut ViewContext<Self>,
1393//     ) -> Task<Result<bool>> {
1394//         if self.project.read(cx).is_read_only() {
1395//             return Task::ready(Ok(true));
1396//         }
1397//         let dirty_items = self
1398//             .panes
1399//             .iter()
1400//             .flat_map(|pane| {
1401//                 pane.read(cx).items().filter_map(|item| {
1402//                     if item.is_dirty(cx) {
1403//                         Some((pane.downgrade(), item.boxed_clone()))
1404//                     } else {
1405//                         None
1406//                     }
1407//                 })
1408//             })
1409//             .collect::<Vec<_>>();
1410
1411//         let project = self.project.clone();
1412//         cx.spawn(|workspace, mut cx| async move {
1413//             // Override save mode and display "Save all files" prompt
1414//             if save_intent == SaveIntent::Close && dirty_items.len() > 1 {
1415//                 let mut answer = workspace.update(&mut cx, |_, cx| {
1416//                     let prompt = Pane::file_names_for_prompt(
1417//                         &mut dirty_items.iter().map(|(_, handle)| handle),
1418//                         dirty_items.len(),
1419//                         cx,
1420//                     );
1421//                     cx.prompt(
1422//                         PromptLevel::Warning,
1423//                         &prompt,
1424//                         &["Save all", "Discard all", "Cancel"],
1425//                     )
1426//                 })?;
1427//                 match answer.next().await {
1428//                     Some(0) => save_intent = SaveIntent::SaveAll,
1429//                     Some(1) => save_intent = SaveIntent::Skip,
1430//                     _ => {}
1431//                 }
1432//             }
1433//             for (pane, item) in dirty_items {
1434//                 let (singleton, project_entry_ids) =
1435//                     cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)));
1436//                 if singleton || !project_entry_ids.is_empty() {
1437//                     if let Some(ix) =
1438//                         pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))?
1439//                     {
1440//                         if !Pane::save_item(
1441//                             project.clone(),
1442//                             &pane,
1443//                             ix,
1444//                             &*item,
1445//                             save_intent,
1446//                             &mut cx,
1447//                         )
1448//                         .await?
1449//                         {
1450//                             return Ok(false);
1451//                         }
1452//                     }
1453//                 }
1454//             }
1455//             Ok(true)
1456//         })
1457//     }
1458
1459//     pub fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
1460//         let mut paths = cx.prompt_for_paths(PathPromptOptions {
1461//             files: true,
1462//             directories: true,
1463//             multiple: true,
1464//         });
1465
1466//         Some(cx.spawn(|this, mut cx| async move {
1467//             if let Some(paths) = paths.recv().await.flatten() {
1468//                 if let Some(task) = this
1469//                     .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx))
1470//                     .log_err()
1471//                 {
1472//                     task.await?
1473//                 }
1474//             }
1475//             Ok(())
1476//         }))
1477//     }
1478
1479//     pub fn open_workspace_for_paths(
1480//         &mut self,
1481//         paths: Vec<PathBuf>,
1482//         cx: &mut ViewContext<Self>,
1483//     ) -> Task<Result<()>> {
1484//         let window = cx.window().downcast::<Self>();
1485//         let is_remote = self.project.read(cx).is_remote();
1486//         let has_worktree = self.project.read(cx).worktrees(cx).next().is_some();
1487//         let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
1488//         let close_task = if is_remote || has_worktree || has_dirty_items {
1489//             None
1490//         } else {
1491//             Some(self.prepare_to_close(false, cx))
1492//         };
1493//         let app_state = self.app_state.clone();
1494
1495//         cx.spawn(|_, mut cx| async move {
1496//             let window_to_replace = if let Some(close_task) = close_task {
1497//                 if !close_task.await? {
1498//                     return Ok(());
1499//                 }
1500//                 window
1501//             } else {
1502//                 None
1503//             };
1504//             cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))
1505//                 .await?;
1506//             Ok(())
1507//         })
1508//     }
1509
1510//     #[allow(clippy::type_complexity)]
1511//     pub fn open_paths(
1512//         &mut self,
1513//         mut abs_paths: Vec<PathBuf>,
1514//         visible: bool,
1515//         cx: &mut ViewContext<Self>,
1516//     ) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>> {
1517//         log::info!("open paths {:?}", abs_paths);
1518
1519//         let fs = self.app_state.fs.clone();
1520
1521//         // Sort the paths to ensure we add worktrees for parents before their children.
1522//         abs_paths.sort_unstable();
1523//         cx.spawn(|this, mut cx| async move {
1524//             let mut tasks = Vec::with_capacity(abs_paths.len());
1525//             for abs_path in &abs_paths {
1526//                 let project_path = match this
1527//                     .update(&mut cx, |this, cx| {
1528//                         Workspace::project_path_for_path(
1529//                             this.project.clone(),
1530//                             abs_path,
1531//                             visible,
1532//                             cx,
1533//                         )
1534//                     })
1535//                     .log_err()
1536//                 {
1537//                     Some(project_path) => project_path.await.log_err(),
1538//                     None => None,
1539//                 };
1540
1541//                 let this = this.clone();
1542//                 let task = cx.spawn(|mut cx| {
1543//                     let fs = fs.clone();
1544//                     let abs_path = abs_path.clone();
1545//                     async move {
1546//                         let (worktree, project_path) = project_path?;
1547//                         if fs.is_file(&abs_path).await {
1548//                             Some(
1549//                                 this.update(&mut cx, |this, cx| {
1550//                                     this.open_path(project_path, None, true, cx)
1551//                                 })
1552//                                 .log_err()?
1553//                                 .await,
1554//                             )
1555//                         } else {
1556//                             this.update(&mut cx, |workspace, cx| {
1557//                                 let worktree = worktree.read(cx);
1558//                                 let worktree_abs_path = worktree.abs_path();
1559//                                 let entry_id = if abs_path == worktree_abs_path.as_ref() {
1560//                                     worktree.root_entry()
1561//                                 } else {
1562//                                     abs_path
1563//                                         .strip_prefix(worktree_abs_path.as_ref())
1564//                                         .ok()
1565//                                         .and_then(|relative_path| {
1566//                                             worktree.entry_for_path(relative_path)
1567//                                         })
1568//                                 }
1569//                                 .map(|entry| entry.id);
1570//                                 if let Some(entry_id) = entry_id {
1571//                                     workspace.project().update(cx, |_, cx| {
1572//                                         cx.emit(project::Event::ActiveEntryChanged(Some(entry_id)));
1573//                                     })
1574//                                 }
1575//                             })
1576//                             .log_err()?;
1577//                             None
1578//                         }
1579//                     }
1580//                 });
1581//                 tasks.push(task);
1582//             }
1583
1584//             futures::future::join_all(tasks).await
1585//         })
1586//     }
1587
1588//     fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext<Self>) {
1589//         let mut paths = cx.prompt_for_paths(PathPromptOptions {
1590//             files: false,
1591//             directories: true,
1592//             multiple: true,
1593//         });
1594//         cx.spawn(|this, mut cx| async move {
1595//             if let Some(paths) = paths.recv().await.flatten() {
1596//                 let results = this
1597//                     .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))?
1598//                     .await;
1599//                 for result in results.into_iter().flatten() {
1600//                     result.log_err();
1601//                 }
1602//             }
1603//             anyhow::Ok(())
1604//         })
1605//         .detach_and_log_err(cx);
1606//     }
1607
1608//     fn project_path_for_path(
1609//         project: ModelHandle<Project>,
1610//         abs_path: &Path,
1611//         visible: bool,
1612//         cx: &mut AppContext,
1613//     ) -> Task<Result<(ModelHandle<Worktree>, ProjectPath)>> {
1614//         let entry = project.update(cx, |project, cx| {
1615//             project.find_or_create_local_worktree(abs_path, visible, cx)
1616//         });
1617//         cx.spawn(|cx| async move {
1618//             let (worktree, path) = entry.await?;
1619//             let worktree_id = worktree.read_with(&cx, |t, _| t.id());
1620//             Ok((
1621//                 worktree,
1622//                 ProjectPath {
1623//                     worktree_id,
1624//                     path: path.into(),
1625//                 },
1626//             ))
1627//         })
1628//     }
1629
1630//     /// Returns the modal that was toggled closed if it was open.
1631//     pub fn toggle_modal<V, F>(
1632//         &mut self,
1633//         cx: &mut ViewContext<Self>,
1634//         add_view: F,
1635//     ) -> Option<ViewHandle<V>>
1636//     where
1637//         V: 'static + Modal,
1638//         F: FnOnce(&mut Self, &mut ViewContext<Self>) -> ViewHandle<V>,
1639//     {
1640//         cx.notify();
1641//         // Whatever modal was visible is getting clobbered. If its the same type as V, then return
1642//         // it. Otherwise, create a new modal and set it as active.
1643//         if let Some(already_open_modal) = self
1644//             .dismiss_modal(cx)
1645//             .and_then(|modal| modal.downcast::<V>())
1646//         {
1647//             cx.focus_self();
1648//             Some(already_open_modal)
1649//         } else {
1650//             let modal = add_view(self, cx);
1651//             cx.subscribe(&modal, |this, _, event, cx| {
1652//                 if V::dismiss_on_event(event) {
1653//                     this.dismiss_modal(cx);
1654//                 }
1655//             })
1656//             .detach();
1657//             let previously_focused_view_id = cx.focused_view_id();
1658//             cx.focus(&modal);
1659//             self.modal = Some(ActiveModal {
1660//                 view: Box::new(modal),
1661//                 previously_focused_view_id,
1662//             });
1663//             None
1664//         }
1665//     }
1666
1667//     pub fn modal<V: 'static + View>(&self) -> Option<ViewHandle<V>> {
1668//         self.modal
1669//             .as_ref()
1670//             .and_then(|modal| modal.view.as_any().clone().downcast::<V>())
1671//     }
1672
1673//     pub fn dismiss_modal(&mut self, cx: &mut ViewContext<Self>) -> Option<AnyViewHandle> {
1674//         if let Some(modal) = self.modal.take() {
1675//             if let Some(previously_focused_view_id) = modal.previously_focused_view_id {
1676//                 if modal.view.has_focus(cx) {
1677//                     cx.window_context().focus(Some(previously_focused_view_id));
1678//                 }
1679//             }
1680//             cx.notify();
1681//             Some(modal.view.as_any().clone())
1682//         } else {
1683//             None
1684//         }
1685//     }
1686
1687//     pub fn items<'a>(
1688//         &'a self,
1689//         cx: &'a AppContext,
1690//     ) -> impl 'a + Iterator<Item = &Box<dyn ItemHandle>> {
1691//         self.panes.iter().flat_map(|pane| pane.read(cx).items())
1692//     }
1693
1694//     pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
1695//         self.items_of_type(cx).max_by_key(|item| item.id())
1696//     }
1697
1698//     pub fn items_of_type<'a, T: Item>(
1699//         &'a self,
1700//         cx: &'a AppContext,
1701//     ) -> impl 'a + Iterator<Item = ViewHandle<T>> {
1702//         self.panes
1703//             .iter()
1704//             .flat_map(|pane| pane.read(cx).items_of_type())
1705//     }
1706
1707//     pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
1708//         self.active_pane().read(cx).active_item()
1709//     }
1710
1711//     fn active_project_path(&self, cx: &ViewContext<Self>) -> Option<ProjectPath> {
1712//         self.active_item(cx).and_then(|item| item.project_path(cx))
1713//     }
1714
1715//     pub fn save_active_item(
1716//         &mut self,
1717//         save_intent: SaveIntent,
1718//         cx: &mut ViewContext<Self>,
1719//     ) -> Task<Result<()>> {
1720//         let project = self.project.clone();
1721//         let pane = self.active_pane();
1722//         let item_ix = pane.read(cx).active_item_index();
1723//         let item = pane.read(cx).active_item();
1724//         let pane = pane.downgrade();
1725
1726//         cx.spawn(|_, mut cx| async move {
1727//             if let Some(item) = item {
1728//                 Pane::save_item(project, &pane, item_ix, item.as_ref(), save_intent, &mut cx)
1729//                     .await
1730//                     .map(|_| ())
1731//             } else {
1732//                 Ok(())
1733//             }
1734//         })
1735//     }
1736
1737//     pub fn close_inactive_items_and_panes(
1738//         &mut self,
1739//         _: &CloseInactiveTabsAndPanes,
1740//         cx: &mut ViewContext<Self>,
1741//     ) -> Option<Task<Result<()>>> {
1742//         self.close_all_internal(true, SaveIntent::Close, cx)
1743//     }
1744
1745//     pub fn close_all_items_and_panes(
1746//         &mut self,
1747//         action: &CloseAllItemsAndPanes,
1748//         cx: &mut ViewContext<Self>,
1749//     ) -> Option<Task<Result<()>>> {
1750//         self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx)
1751//     }
1752
1753//     fn close_all_internal(
1754//         &mut self,
1755//         retain_active_pane: bool,
1756//         save_intent: SaveIntent,
1757//         cx: &mut ViewContext<Self>,
1758//     ) -> Option<Task<Result<()>>> {
1759//         let current_pane = self.active_pane();
1760
1761//         let mut tasks = Vec::new();
1762
1763//         if retain_active_pane {
1764//             if let Some(current_pane_close) = current_pane.update(cx, |pane, cx| {
1765//                 pane.close_inactive_items(&CloseInactiveItems, cx)
1766//             }) {
1767//                 tasks.push(current_pane_close);
1768//             };
1769//         }
1770
1771//         for pane in self.panes() {
1772//             if retain_active_pane && pane.id() == current_pane.id() {
1773//                 continue;
1774//             }
1775
1776//             if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| {
1777//                 pane.close_all_items(
1778//                     &CloseAllItems {
1779//                         save_intent: Some(save_intent),
1780//                     },
1781//                     cx,
1782//                 )
1783//             }) {
1784//                 tasks.push(close_pane_items)
1785//             }
1786//         }
1787
1788//         if tasks.is_empty() {
1789//             None
1790//         } else {
1791//             Some(cx.spawn(|_, _| async move {
1792//                 for task in tasks {
1793//                     task.await?
1794//                 }
1795//                 Ok(())
1796//             }))
1797//         }
1798//     }
1799
1800//     pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
1801//         let dock = match dock_side {
1802//             DockPosition::Left => &self.left_dock,
1803//             DockPosition::Bottom => &self.bottom_dock,
1804//             DockPosition::Right => &self.right_dock,
1805//         };
1806//         let mut focus_center = false;
1807//         let mut reveal_dock = false;
1808//         dock.update(cx, |dock, cx| {
1809//             let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
1810//             let was_visible = dock.is_open() && !other_is_zoomed;
1811//             dock.set_open(!was_visible, cx);
1812
1813//             if let Some(active_panel) = dock.active_panel() {
1814//                 if was_visible {
1815//                     if active_panel.has_focus(cx) {
1816//                         focus_center = true;
1817//                     }
1818//                 } else {
1819//                     cx.focus(active_panel.as_any());
1820//                     reveal_dock = true;
1821//                 }
1822//             }
1823//         });
1824
1825//         if reveal_dock {
1826//             self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx);
1827//         }
1828
1829//         if focus_center {
1830//             cx.focus_self();
1831//         }
1832
1833//         cx.notify();
1834//         self.serialize_workspace(cx);
1835//     }
1836
1837//     pub fn close_all_docks(&mut self, cx: &mut ViewContext<Self>) {
1838//         let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock];
1839
1840//         for dock in docks {
1841//             dock.update(cx, |dock, cx| {
1842//                 dock.set_open(false, cx);
1843//             });
1844//         }
1845
1846//         cx.focus_self();
1847//         cx.notify();
1848//         self.serialize_workspace(cx);
1849//     }
1850
1851//     /// Transfer focus to the panel of the given type.
1852//     pub fn focus_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) -> Option<ViewHandle<T>> {
1853//         self.focus_or_unfocus_panel::<T>(cx, |_, _| true)?
1854//             .as_any()
1855//             .clone()
1856//             .downcast()
1857//     }
1858
1859//     /// Focus the panel of the given type if it isn't already focused. If it is
1860//     /// already focused, then transfer focus back to the workspace center.
1861//     pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
1862//         self.focus_or_unfocus_panel::<T>(cx, |panel, cx| !panel.has_focus(cx));
1863//     }
1864
1865//     /// Focus or unfocus the given panel type, depending on the given callback.
1866//     fn focus_or_unfocus_panel<T: Panel>(
1867//         &mut self,
1868//         cx: &mut ViewContext<Self>,
1869//         should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext<Dock>) -> bool,
1870//     ) -> Option<Rc<dyn PanelHandle>> {
1871//         for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
1872//             if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
1873//                 let mut focus_center = false;
1874//                 let mut reveal_dock = false;
1875//                 let panel = dock.update(cx, |dock, cx| {
1876//                     dock.activate_panel(panel_index, cx);
1877
1878//                     let panel = dock.active_panel().cloned();
1879//                     if let Some(panel) = panel.as_ref() {
1880//                         if should_focus(&**panel, cx) {
1881//                             dock.set_open(true, cx);
1882//                             cx.focus(panel.as_any());
1883//                             reveal_dock = true;
1884//                         } else {
1885//                             // if panel.is_zoomed(cx) {
1886//                             //     dock.set_open(false, cx);
1887//                             // }
1888//                             focus_center = true;
1889//                         }
1890//                     }
1891//                     panel
1892//                 });
1893
1894//                 if focus_center {
1895//                     cx.focus_self();
1896//                 }
1897
1898//                 self.serialize_workspace(cx);
1899//                 cx.notify();
1900//                 return panel;
1901//             }
1902//         }
1903//         None
1904//     }
1905
1906//     pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<ViewHandle<T>> {
1907//         for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
1908//             let dock = dock.read(cx);
1909//             if let Some(panel) = dock.panel::<T>() {
1910//                 return Some(panel);
1911//             }
1912//         }
1913//         None
1914//     }
1915
1916//     fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
1917//         for pane in &self.panes {
1918//             pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
1919//         }
1920
1921//         self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1922//         self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1923//         self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1924//         self.zoomed = None;
1925//         self.zoomed_position = None;
1926
1927//         cx.notify();
1928//     }
1929
1930//     #[cfg(any(test, feature = "test-support"))]
1931//     pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
1932//         self.zoomed.and_then(|view| view.upgrade(cx))
1933//     }
1934
1935//     fn dismiss_zoomed_items_to_reveal(
1936//         &mut self,
1937//         dock_to_reveal: Option<DockPosition>,
1938//         cx: &mut ViewContext<Self>,
1939//     ) {
1940//         // If a center pane is zoomed, unzoom it.
1941//         for pane in &self.panes {
1942//             if pane != &self.active_pane || dock_to_reveal.is_some() {
1943//                 pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
1944//             }
1945//         }
1946
1947//         // If another dock is zoomed, hide it.
1948//         let mut focus_center = false;
1949//         for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] {
1950//             dock.update(cx, |dock, cx| {
1951//                 if Some(dock.position()) != dock_to_reveal {
1952//                     if let Some(panel) = dock.active_panel() {
1953//                         if panel.is_zoomed(cx) {
1954//                             focus_center |= panel.has_focus(cx);
1955//                             dock.set_open(false, cx);
1956//                         }
1957//                     }
1958//                 }
1959//             });
1960//         }
1961
1962//         if focus_center {
1963//             cx.focus_self();
1964//         }
1965
1966//         if self.zoomed_position != dock_to_reveal {
1967//             self.zoomed = None;
1968//             self.zoomed_position = None;
1969//         }
1970
1971//         cx.notify();
1972//     }
1973
1974//     fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
1975//         let pane = cx.add_view(|cx| {
1976//             Pane::new(
1977//                 self.weak_handle(),
1978//                 self.project.clone(),
1979//                 self.app_state.background_actions,
1980//                 self.pane_history_timestamp.clone(),
1981//                 cx,
1982//             )
1983//         });
1984//         cx.subscribe(&pane, Self::handle_pane_event).detach();
1985//         self.panes.push(pane.clone());
1986//         cx.focus(&pane);
1987//         cx.emit(Event::PaneAdded(pane.clone()));
1988//         pane
1989//     }
1990
1991//     pub fn add_item_to_center(
1992//         &mut self,
1993//         item: Box<dyn ItemHandle>,
1994//         cx: &mut ViewContext<Self>,
1995//     ) -> bool {
1996//         if let Some(center_pane) = self.last_active_center_pane.clone() {
1997//             if let Some(center_pane) = center_pane.upgrade(cx) {
1998//                 center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
1999//                 true
2000//             } else {
2001//                 false
2002//             }
2003//         } else {
2004//             false
2005//         }
2006//     }
2007
2008//     pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
2009//         self.active_pane
2010//             .update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
2011//     }
2012
2013//     pub fn split_item(
2014//         &mut self,
2015//         split_direction: SplitDirection,
2016//         item: Box<dyn ItemHandle>,
2017//         cx: &mut ViewContext<Self>,
2018//     ) {
2019//         let new_pane = self.split_pane(self.active_pane.clone(), split_direction, cx);
2020//         new_pane.update(cx, move |new_pane, cx| {
2021//             new_pane.add_item(item, true, true, None, cx)
2022//         })
2023//     }
2024
2025//     pub fn open_abs_path(
2026//         &mut self,
2027//         abs_path: PathBuf,
2028//         visible: bool,
2029//         cx: &mut ViewContext<Self>,
2030//     ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
2031//         cx.spawn(|workspace, mut cx| async move {
2032//             let open_paths_task_result = workspace
2033//                 .update(&mut cx, |workspace, cx| {
2034//                     workspace.open_paths(vec![abs_path.clone()], visible, cx)
2035//                 })
2036//                 .with_context(|| format!("open abs path {abs_path:?} task spawn"))?
2037//                 .await;
2038//             anyhow::ensure!(
2039//                 open_paths_task_result.len() == 1,
2040//                 "open abs path {abs_path:?} task returned incorrect number of results"
2041//             );
2042//             match open_paths_task_result
2043//                 .into_iter()
2044//                 .next()
2045//                 .expect("ensured single task result")
2046//             {
2047//                 Some(open_result) => {
2048//                     open_result.with_context(|| format!("open abs path {abs_path:?} task join"))
2049//                 }
2050//                 None => anyhow::bail!("open abs path {abs_path:?} task returned None"),
2051//             }
2052//         })
2053//     }
2054
2055//     pub fn split_abs_path(
2056//         &mut self,
2057//         abs_path: PathBuf,
2058//         visible: bool,
2059//         cx: &mut ViewContext<Self>,
2060//     ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
2061//         let project_path_task =
2062//             Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx);
2063//         cx.spawn(|this, mut cx| async move {
2064//             let (_, path) = project_path_task.await?;
2065//             this.update(&mut cx, |this, cx| this.split_path(path, cx))?
2066//                 .await
2067//         })
2068//     }
2069
2070//     pub fn open_path(
2071//         &mut self,
2072//         path: impl Into<ProjectPath>,
2073//         pane: Option<WeakViewHandle<Pane>>,
2074//         focus_item: bool,
2075//         cx: &mut ViewContext<Self>,
2076//     ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
2077//         let pane = pane.unwrap_or_else(|| {
2078//             self.last_active_center_pane.clone().unwrap_or_else(|| {
2079//                 self.panes
2080//                     .first()
2081//                     .expect("There must be an active pane")
2082//                     .downgrade()
2083//             })
2084//         });
2085
2086//         let task = self.load_path(path.into(), cx);
2087//         cx.spawn(|_, mut cx| async move {
2088//             let (project_entry_id, build_item) = task.await?;
2089//             pane.update(&mut cx, |pane, cx| {
2090//                 pane.open_item(project_entry_id, focus_item, cx, build_item)
2091//             })
2092//         })
2093//     }
2094
2095//     pub fn split_path(
2096//         &mut self,
2097//         path: impl Into<ProjectPath>,
2098//         cx: &mut ViewContext<Self>,
2099//     ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
2100//         let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
2101//             self.panes
2102//                 .first()
2103//                 .expect("There must be an active pane")
2104//                 .downgrade()
2105//         });
2106
2107//         if let Member::Pane(center_pane) = &self.center.root {
2108//             if center_pane.read(cx).items_len() == 0 {
2109//                 return self.open_path(path, Some(pane), true, cx);
2110//             }
2111//         }
2112
2113//         let task = self.load_path(path.into(), cx);
2114//         cx.spawn(|this, mut cx| async move {
2115//             let (project_entry_id, build_item) = task.await?;
2116//             this.update(&mut cx, move |this, cx| -> Option<_> {
2117//                 let pane = pane.upgrade(cx)?;
2118//                 let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
2119//                 new_pane.update(cx, |new_pane, cx| {
2120//                     Some(new_pane.open_item(project_entry_id, true, cx, build_item))
2121//                 })
2122//             })
2123//             .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?
2124//         })
2125//     }
2126
2127//     pub(crate) fn load_path(
2128//         &mut self,
2129//         path: ProjectPath,
2130//         cx: &mut ViewContext<Self>,
2131//     ) -> Task<
2132//         Result<(
2133//             ProjectEntryId,
2134//             impl 'static + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
2135//         )>,
2136//     > {
2137//         let project = self.project().clone();
2138//         let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
2139//         cx.spawn(|_, mut cx| async move {
2140//             let (project_entry_id, project_item) = project_item.await?;
2141//             let build_item = cx.update(|cx| {
2142//                 cx.default_global::<ProjectItemBuilders>()
2143//                     .get(&project_item.model_type())
2144//                     .ok_or_else(|| anyhow!("no item builder for project item"))
2145//                     .cloned()
2146//             })?;
2147//             let build_item =
2148//                 move |cx: &mut ViewContext<Pane>| build_item(project, project_item, cx);
2149//             Ok((project_entry_id, build_item))
2150//         })
2151//     }
2152
2153//     pub fn open_project_item<T>(
2154//         &mut self,
2155//         project_item: ModelHandle<T::Item>,
2156//         cx: &mut ViewContext<Self>,
2157//     ) -> ViewHandle<T>
2158//     where
2159//         T: ProjectItem,
2160//     {
2161//         use project::Item as _;
2162
2163//         let entry_id = project_item.read(cx).entry_id(cx);
2164//         if let Some(item) = entry_id
2165//             .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
2166//             .and_then(|item| item.downcast())
2167//         {
2168//             self.activate_item(&item, cx);
2169//             return item;
2170//         }
2171
2172//         let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
2173//         self.add_item(Box::new(item.clone()), cx);
2174//         item
2175//     }
2176
2177//     pub fn split_project_item<T>(
2178//         &mut self,
2179//         project_item: ModelHandle<T::Item>,
2180//         cx: &mut ViewContext<Self>,
2181//     ) -> ViewHandle<T>
2182//     where
2183//         T: ProjectItem,
2184//     {
2185//         use project::Item as _;
2186
2187//         let entry_id = project_item.read(cx).entry_id(cx);
2188//         if let Some(item) = entry_id
2189//             .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
2190//             .and_then(|item| item.downcast())
2191//         {
2192//             self.activate_item(&item, cx);
2193//             return item;
2194//         }
2195
2196//         let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
2197//         self.split_item(SplitDirection::Right, Box::new(item.clone()), cx);
2198//         item
2199//     }
2200
2201//     pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
2202//         if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) {
2203//             self.active_pane.update(cx, |pane, cx| {
2204//                 pane.add_item(Box::new(shared_screen), false, true, None, cx)
2205//             });
2206//         }
2207//     }
2208
2209//     pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
2210//         let result = self.panes.iter().find_map(|pane| {
2211//             pane.read(cx)
2212//                 .index_for_item(item)
2213//                 .map(|ix| (pane.clone(), ix))
2214//         });
2215//         if let Some((pane, ix)) = result {
2216//             pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, cx));
2217//             true
2218//         } else {
2219//             false
2220//         }
2221//     }
2222
2223//     fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext<Self>) {
2224//         let panes = self.center.panes();
2225//         if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
2226//             cx.focus(&pane);
2227//         } else {
2228//             self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx);
2229//         }
2230//     }
2231
2232//     pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
2233//         let panes = self.center.panes();
2234//         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
2235//             let next_ix = (ix + 1) % panes.len();
2236//             let next_pane = panes[next_ix].clone();
2237//             cx.focus(&next_pane);
2238//         }
2239//     }
2240
2241//     pub fn activate_previous_pane(&mut self, cx: &mut ViewContext<Self>) {
2242//         let panes = self.center.panes();
2243//         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
2244//             let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
2245//             let prev_pane = panes[prev_ix].clone();
2246//             cx.focus(&prev_pane);
2247//         }
2248//     }
2249
2250//     pub fn activate_pane_in_direction(
2251//         &mut self,
2252//         direction: SplitDirection,
2253//         cx: &mut ViewContext<Self>,
2254//     ) {
2255//         if let Some(pane) = self.find_pane_in_direction(direction, cx) {
2256//             cx.focus(pane);
2257//         }
2258//     }
2259
2260//     pub fn swap_pane_in_direction(
2261//         &mut self,
2262//         direction: SplitDirection,
2263//         cx: &mut ViewContext<Self>,
2264//     ) {
2265//         if let Some(to) = self
2266//             .find_pane_in_direction(direction, cx)
2267//             .map(|pane| pane.clone())
2268//         {
2269//             self.center.swap(&self.active_pane.clone(), &to);
2270//             cx.notify();
2271//         }
2272//     }
2273
2274//     fn find_pane_in_direction(
2275//         &mut self,
2276//         direction: SplitDirection,
2277//         cx: &mut ViewContext<Self>,
2278//     ) -> Option<&ViewHandle<Pane>> {
2279//         let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else {
2280//             return None;
2281//         };
2282//         let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx);
2283//         let center = match cursor {
2284//             Some(cursor) if bounding_box.contains_point(cursor) => cursor,
2285//             _ => bounding_box.center(),
2286//         };
2287
2288//         let distance_to_next = theme::current(cx).workspace.pane_divider.width + 1.;
2289
2290//         let target = match direction {
2291//             SplitDirection::Left => vec2f(bounding_box.origin_x() - distance_to_next, center.y()),
2292//             SplitDirection::Right => vec2f(bounding_box.max_x() + distance_to_next, center.y()),
2293//             SplitDirection::Up => vec2f(center.x(), bounding_box.origin_y() - distance_to_next),
2294//             SplitDirection::Down => vec2f(center.x(), bounding_box.max_y() + distance_to_next),
2295//         };
2296//         self.center.pane_at_pixel_position(target)
2297//     }
2298
2299//     fn handle_pane_focused(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
2300//         if self.active_pane != pane {
2301//             self.active_pane = pane.clone();
2302//             self.status_bar.update(cx, |status_bar, cx| {
2303//                 status_bar.set_active_pane(&self.active_pane, cx);
2304//             });
2305//             self.active_item_path_changed(cx);
2306//             self.last_active_center_pane = Some(pane.downgrade());
2307//         }
2308
2309//         self.dismiss_zoomed_items_to_reveal(None, cx);
2310//         if pane.read(cx).is_zoomed() {
2311//             self.zoomed = Some(pane.downgrade().into_any());
2312//         } else {
2313//             self.zoomed = None;
2314//         }
2315//         self.zoomed_position = None;
2316//         self.update_active_view_for_followers(cx);
2317
2318//         cx.notify();
2319//     }
2320
2321//     fn handle_pane_event(
2322//         &mut self,
2323//         pane: ViewHandle<Pane>,
2324//         event: &pane::Event,
2325//         cx: &mut ViewContext<Self>,
2326//     ) {
2327//         match event {
2328//             pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx),
2329//             pane::Event::Split(direction) => {
2330//                 self.split_and_clone(pane, *direction, cx);
2331//             }
2332//             pane::Event::Remove => self.remove_pane(pane, cx),
2333//             pane::Event::ActivateItem { local } => {
2334//                 if *local {
2335//                     self.unfollow(&pane, cx);
2336//                 }
2337//                 if &pane == self.active_pane() {
2338//                     self.active_item_path_changed(cx);
2339//                 }
2340//             }
2341//             pane::Event::ChangeItemTitle => {
2342//                 if pane == self.active_pane {
2343//                     self.active_item_path_changed(cx);
2344//                 }
2345//                 self.update_window_edited(cx);
2346//             }
2347//             pane::Event::RemoveItem { item_id } => {
2348//                 self.update_window_edited(cx);
2349//                 if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) {
2350//                     if entry.get().id() == pane.id() {
2351//                         entry.remove();
2352//                     }
2353//                 }
2354//             }
2355//             pane::Event::Focus => {
2356//                 self.handle_pane_focused(pane.clone(), cx);
2357//             }
2358//             pane::Event::ZoomIn => {
2359//                 if pane == self.active_pane {
2360//                     pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
2361//                     if pane.read(cx).has_focus() {
2362//                         self.zoomed = Some(pane.downgrade().into_any());
2363//                         self.zoomed_position = None;
2364//                     }
2365//                     cx.notify();
2366//                 }
2367//             }
2368//             pane::Event::ZoomOut => {
2369//                 pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
2370//                 if self.zoomed_position.is_none() {
2371//                     self.zoomed = None;
2372//                 }
2373//                 cx.notify();
2374//             }
2375//         }
2376
2377//         self.serialize_workspace(cx);
2378//     }
2379
2380//     pub fn split_pane(
2381//         &mut self,
2382//         pane_to_split: ViewHandle<Pane>,
2383//         split_direction: SplitDirection,
2384//         cx: &mut ViewContext<Self>,
2385//     ) -> ViewHandle<Pane> {
2386//         let new_pane = self.add_pane(cx);
2387//         self.center
2388//             .split(&pane_to_split, &new_pane, split_direction)
2389//             .unwrap();
2390//         cx.notify();
2391//         new_pane
2392//     }
2393
2394//     pub fn split_and_clone(
2395//         &mut self,
2396//         pane: ViewHandle<Pane>,
2397//         direction: SplitDirection,
2398//         cx: &mut ViewContext<Self>,
2399//     ) -> Option<ViewHandle<Pane>> {
2400//         let item = pane.read(cx).active_item()?;
2401//         let maybe_pane_handle = if let Some(clone) = item.clone_on_split(self.database_id(), cx) {
2402//             let new_pane = self.add_pane(cx);
2403//             new_pane.update(cx, |pane, cx| pane.add_item(clone, true, true, None, cx));
2404//             self.center.split(&pane, &new_pane, direction).unwrap();
2405//             Some(new_pane)
2406//         } else {
2407//             None
2408//         };
2409//         cx.notify();
2410//         maybe_pane_handle
2411//     }
2412
2413//     pub fn split_pane_with_item(
2414//         &mut self,
2415//         pane_to_split: WeakViewHandle<Pane>,
2416//         split_direction: SplitDirection,
2417//         from: WeakViewHandle<Pane>,
2418//         item_id_to_move: usize,
2419//         cx: &mut ViewContext<Self>,
2420//     ) {
2421//         let Some(pane_to_split) = pane_to_split.upgrade(cx) else {
2422//             return;
2423//         };
2424//         let Some(from) = from.upgrade(cx) else {
2425//             return;
2426//         };
2427
2428//         let new_pane = self.add_pane(cx);
2429//         self.move_item(from.clone(), new_pane.clone(), item_id_to_move, 0, cx);
2430//         self.center
2431//             .split(&pane_to_split, &new_pane, split_direction)
2432//             .unwrap();
2433//         cx.notify();
2434//     }
2435
2436//     pub fn split_pane_with_project_entry(
2437//         &mut self,
2438//         pane_to_split: WeakViewHandle<Pane>,
2439//         split_direction: SplitDirection,
2440//         project_entry: ProjectEntryId,
2441//         cx: &mut ViewContext<Self>,
2442//     ) -> Option<Task<Result<()>>> {
2443//         let pane_to_split = pane_to_split.upgrade(cx)?;
2444//         let new_pane = self.add_pane(cx);
2445//         self.center
2446//             .split(&pane_to_split, &new_pane, split_direction)
2447//             .unwrap();
2448
2449//         let path = self.project.read(cx).path_for_entry(project_entry, cx)?;
2450//         let task = self.open_path(path, Some(new_pane.downgrade()), true, cx);
2451//         Some(cx.foreground().spawn(async move {
2452//             task.await?;
2453//             Ok(())
2454//         }))
2455//     }
2456
2457//     pub fn move_item(
2458//         &mut self,
2459//         source: ViewHandle<Pane>,
2460//         destination: ViewHandle<Pane>,
2461//         item_id_to_move: usize,
2462//         destination_index: usize,
2463//         cx: &mut ViewContext<Self>,
2464//     ) {
2465//         let item_to_move = source
2466//             .read(cx)
2467//             .items()
2468//             .enumerate()
2469//             .find(|(_, item_handle)| item_handle.id() == item_id_to_move);
2470
2471//         if item_to_move.is_none() {
2472//             log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop");
2473//             return;
2474//         }
2475//         let (item_ix, item_handle) = item_to_move.unwrap();
2476//         let item_handle = item_handle.clone();
2477
2478//         if source != destination {
2479//             // Close item from previous pane
2480//             source.update(cx, |source, cx| {
2481//                 source.remove_item(item_ix, false, cx);
2482//             });
2483//         }
2484
2485//         // This automatically removes duplicate items in the pane
2486//         destination.update(cx, |destination, cx| {
2487//             destination.add_item(item_handle, true, true, Some(destination_index), cx);
2488//             cx.focus_self();
2489//         });
2490//     }
2491
2492//     fn remove_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
2493//         if self.center.remove(&pane).unwrap() {
2494//             self.force_remove_pane(&pane, cx);
2495//             self.unfollow(&pane, cx);
2496//             self.last_leaders_by_pane.remove(&pane.downgrade());
2497//             for removed_item in pane.read(cx).items() {
2498//                 self.panes_by_item.remove(&removed_item.id());
2499//             }
2500
2501//             cx.notify();
2502//         } else {
2503//             self.active_item_path_changed(cx);
2504//         }
2505//     }
2506
2507//     pub fn panes(&self) -> &[ViewHandle<Pane>] {
2508//         &self.panes
2509//     }
2510
2511//     pub fn active_pane(&self) -> &ViewHandle<Pane> {
2512//         &self.active_pane
2513//     }
2514
2515//     fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
2516//         self.follower_states.retain(|_, state| {
2517//             if state.leader_id == peer_id {
2518//                 for item in state.items_by_leader_view_id.values() {
2519//                     item.set_leader_peer_id(None, cx);
2520//                 }
2521//                 false
2522//             } else {
2523//                 true
2524//             }
2525//         });
2526//         cx.notify();
2527//     }
2528
2529//     fn start_following(
2530//         &mut self,
2531//         leader_id: PeerId,
2532//         cx: &mut ViewContext<Self>,
2533//     ) -> Option<Task<Result<()>>> {
2534//         let pane = self.active_pane().clone();
2535
2536//         self.last_leaders_by_pane
2537//             .insert(pane.downgrade(), leader_id);
2538//         self.unfollow(&pane, cx);
2539//         self.follower_states.insert(
2540//             pane.clone(),
2541//             FollowerState {
2542//                 leader_id,
2543//                 active_view_id: None,
2544//                 items_by_leader_view_id: Default::default(),
2545//             },
2546//         );
2547//         cx.notify();
2548
2549//         let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
2550//         let project_id = self.project.read(cx).remote_id();
2551//         let request = self.app_state.client.request(proto::Follow {
2552//             room_id,
2553//             project_id,
2554//             leader_id: Some(leader_id),
2555//         });
2556
2557//         Some(cx.spawn(|this, mut cx| async move {
2558//             let response = request.await?;
2559//             this.update(&mut cx, |this, _| {
2560//                 let state = this
2561//                     .follower_states
2562//                     .get_mut(&pane)
2563//                     .ok_or_else(|| anyhow!("following interrupted"))?;
2564//                 state.active_view_id = if let Some(active_view_id) = response.active_view_id {
2565//                     Some(ViewId::from_proto(active_view_id)?)
2566//                 } else {
2567//                     None
2568//                 };
2569//                 Ok::<_, anyhow::Error>(())
2570//             })??;
2571//             Self::add_views_from_leader(
2572//                 this.clone(),
2573//                 leader_id,
2574//                 vec![pane],
2575//                 response.views,
2576//                 &mut cx,
2577//             )
2578//             .await?;
2579//             this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?;
2580//             Ok(())
2581//         }))
2582//     }
2583
2584//     pub fn follow_next_collaborator(
2585//         &mut self,
2586//         _: &FollowNextCollaborator,
2587//         cx: &mut ViewContext<Self>,
2588//     ) -> Option<Task<Result<()>>> {
2589//         let collaborators = self.project.read(cx).collaborators();
2590//         let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
2591//             let mut collaborators = collaborators.keys().copied();
2592//             for peer_id in collaborators.by_ref() {
2593//                 if peer_id == leader_id {
2594//                     break;
2595//                 }
2596//             }
2597//             collaborators.next()
2598//         } else if let Some(last_leader_id) =
2599//             self.last_leaders_by_pane.get(&self.active_pane.downgrade())
2600//         {
2601//             if collaborators.contains_key(last_leader_id) {
2602//                 Some(*last_leader_id)
2603//             } else {
2604//                 None
2605//             }
2606//         } else {
2607//             None
2608//         };
2609
2610//         let pane = self.active_pane.clone();
2611//         let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
2612//         else {
2613//             return None;
2614//         };
2615//         if Some(leader_id) == self.unfollow(&pane, cx) {
2616//             return None;
2617//         }
2618//         self.follow(leader_id, cx)
2619//     }
2620
2621//     pub fn follow(
2622//         &mut self,
2623//         leader_id: PeerId,
2624//         cx: &mut ViewContext<Self>,
2625//     ) -> Option<Task<Result<()>>> {
2626//         let room = ActiveCall::global(cx).read(cx).room()?.read(cx);
2627//         let project = self.project.read(cx);
2628
2629//         let Some(remote_participant) = room.remote_participant_for_peer_id(leader_id) else {
2630//             return None;
2631//         };
2632
2633//         let other_project_id = match remote_participant.location {
2634//             call::ParticipantLocation::External => None,
2635//             call::ParticipantLocation::UnsharedProject => None,
2636//             call::ParticipantLocation::SharedProject { project_id } => {
2637//                 if Some(project_id) == project.remote_id() {
2638//                     None
2639//                 } else {
2640//                     Some(project_id)
2641//                 }
2642//             }
2643//         };
2644
2645//         // if they are active in another project, follow there.
2646//         if let Some(project_id) = other_project_id {
2647//             let app_state = self.app_state.clone();
2648//             return Some(crate::join_remote_project(
2649//                 project_id,
2650//                 remote_participant.user.id,
2651//                 app_state,
2652//                 cx,
2653//             ));
2654//         }
2655
2656//         // if you're already following, find the right pane and focus it.
2657//         for (pane, state) in &self.follower_states {
2658//             if leader_id == state.leader_id {
2659//                 cx.focus(pane);
2660//                 return None;
2661//             }
2662//         }
2663
2664//         // Otherwise, follow.
2665//         self.start_following(leader_id, cx)
2666//     }
2667
2668//     pub fn unfollow(
2669//         &mut self,
2670//         pane: &ViewHandle<Pane>,
2671//         cx: &mut ViewContext<Self>,
2672//     ) -> Option<PeerId> {
2673//         let state = self.follower_states.remove(pane)?;
2674//         let leader_id = state.leader_id;
2675//         for (_, item) in state.items_by_leader_view_id {
2676//             item.set_leader_peer_id(None, cx);
2677//         }
2678
2679//         if self
2680//             .follower_states
2681//             .values()
2682//             .all(|state| state.leader_id != state.leader_id)
2683//         {
2684//             let project_id = self.project.read(cx).remote_id();
2685//             let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
2686//             self.app_state
2687//                 .client
2688//                 .send(proto::Unfollow {
2689//                     room_id,
2690//                     project_id,
2691//                     leader_id: Some(leader_id),
2692//                 })
2693//                 .log_err();
2694//         }
2695
2696//         cx.notify();
2697//         Some(leader_id)
2698//     }
2699
2700//     pub fn is_being_followed(&self, peer_id: PeerId) -> bool {
2701//         self.follower_states
2702//             .values()
2703//             .any(|state| state.leader_id == peer_id)
2704//     }
2705
2706//     fn render_titlebar(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
2707//         // TODO: There should be a better system in place for this
2708//         // (https://github.com/zed-industries/zed/issues/1290)
2709//         let is_fullscreen = cx.window_is_fullscreen();
2710//         let container_theme = if is_fullscreen {
2711//             let mut container_theme = theme.titlebar.container;
2712//             container_theme.padding.left = container_theme.padding.right;
2713//             container_theme
2714//         } else {
2715//             theme.titlebar.container
2716//         };
2717
2718//         enum TitleBar {}
2719//         MouseEventHandler::new::<TitleBar, _>(0, cx, |_, cx| {
2720//             Stack::new()
2721//                 .with_children(
2722//                     self.titlebar_item
2723//                         .as_ref()
2724//                         .map(|item| ChildView::new(item, cx)),
2725//                 )
2726//                 .contained()
2727//                 .with_style(container_theme)
2728//         })
2729//         .on_click(MouseButton::Left, |event, _, cx| {
2730//             if event.click_count == 2 {
2731//                 cx.zoom_window();
2732//             }
2733//         })
2734//         .constrained()
2735//         .with_height(theme.titlebar.height)
2736//         .into_any_named("titlebar")
2737//     }
2738
2739//     fn active_item_path_changed(&mut self, cx: &mut ViewContext<Self>) {
2740//         let active_entry = self.active_project_path(cx);
2741//         self.project
2742//             .update(cx, |project, cx| project.set_active_path(active_entry, cx));
2743//         self.update_window_title(cx);
2744//     }
2745
2746//     fn update_window_title(&mut self, cx: &mut ViewContext<Self>) {
2747//         let project = self.project().read(cx);
2748//         let mut title = String::new();
2749
2750//         if let Some(path) = self.active_item(cx).and_then(|item| item.project_path(cx)) {
2751//             let filename = path
2752//                 .path
2753//                 .file_name()
2754//                 .map(|s| s.to_string_lossy())
2755//                 .or_else(|| {
2756//                     Some(Cow::Borrowed(
2757//                         project
2758//                             .worktree_for_id(path.worktree_id, cx)?
2759//                             .read(cx)
2760//                             .root_name(),
2761//                     ))
2762//                 });
2763
2764//             if let Some(filename) = filename {
2765//                 title.push_str(filename.as_ref());
2766//                 title.push_str(" β€” ");
2767//             }
2768//         }
2769
2770//         for (i, name) in project.worktree_root_names(cx).enumerate() {
2771//             if i > 0 {
2772//                 title.push_str(", ");
2773//             }
2774//             title.push_str(name);
2775//         }
2776
2777//         if title.is_empty() {
2778//             title = "empty project".to_string();
2779//         }
2780
2781//         if project.is_remote() {
2782//             title.push_str(" ↙");
2783//         } else if project.is_shared() {
2784//             title.push_str(" β†—");
2785//         }
2786
2787//         cx.set_window_title(&title);
2788//     }
2789
2790//     fn update_window_edited(&mut self, cx: &mut ViewContext<Self>) {
2791//         let is_edited = !self.project.read(cx).is_read_only()
2792//             && self
2793//                 .items(cx)
2794//                 .any(|item| item.has_conflict(cx) || item.is_dirty(cx));
2795//         if is_edited != self.window_edited {
2796//             self.window_edited = is_edited;
2797//             cx.set_window_edited(self.window_edited)
2798//         }
2799//     }
2800
2801//     fn render_disconnected_overlay(
2802//         &self,
2803//         cx: &mut ViewContext<Workspace>,
2804//     ) -> Option<AnyElement<Workspace>> {
2805//         if self.project.read(cx).is_read_only() {
2806//             enum DisconnectedOverlay {}
2807//             Some(
2808//                 MouseEventHandler::new::<DisconnectedOverlay, _>(0, cx, |_, cx| {
2809//                     let theme = &theme::current(cx);
2810//                     Label::new(
2811//                         "Your connection to the remote project has been lost.",
2812//                         theme.workspace.disconnected_overlay.text.clone(),
2813//                     )
2814//                     .aligned()
2815//                     .contained()
2816//                     .with_style(theme.workspace.disconnected_overlay.container)
2817//                 })
2818//                 .with_cursor_style(CursorStyle::Arrow)
2819//                 .capture_all()
2820//                 .into_any_named("disconnected overlay"),
2821//             )
2822//         } else {
2823//             None
2824//         }
2825//     }
2826
2827//     fn render_notifications(
2828//         &self,
2829//         theme: &theme::Workspace,
2830//         cx: &AppContext,
2831//     ) -> Option<AnyElement<Workspace>> {
2832//         if self.notifications.is_empty() {
2833//             None
2834//         } else {
2835//             Some(
2836//                 Flex::column()
2837//                     .with_children(self.notifications.iter().map(|(_, _, notification)| {
2838//                         ChildView::new(notification.as_any(), cx)
2839//                             .contained()
2840//                             .with_style(theme.notification)
2841//                     }))
2842//                     .constrained()
2843//                     .with_width(theme.notifications.width)
2844//                     .contained()
2845//                     .with_style(theme.notifications.container)
2846//                     .aligned()
2847//                     .bottom()
2848//                     .right()
2849//                     .into_any(),
2850//             )
2851//         }
2852//     }
2853
2854//     // RPC handlers
2855
2856//     fn handle_follow(
2857//         &mut self,
2858//         follower_project_id: Option<u64>,
2859//         cx: &mut ViewContext<Self>,
2860//     ) -> proto::FollowResponse {
2861//         let client = &self.app_state.client;
2862//         let project_id = self.project.read(cx).remote_id();
2863
2864//         let active_view_id = self.active_item(cx).and_then(|i| {
2865//             Some(
2866//                 i.to_followable_item_handle(cx)?
2867//                     .remote_id(client, cx)?
2868//                     .to_proto(),
2869//             )
2870//         });
2871
2872//         cx.notify();
2873
2874//         self.last_active_view_id = active_view_id.clone();
2875//         proto::FollowResponse {
2876//             active_view_id,
2877//             views: self
2878//                 .panes()
2879//                 .iter()
2880//                 .flat_map(|pane| {
2881//                     let leader_id = self.leader_for_pane(pane);
2882//                     pane.read(cx).items().filter_map({
2883//                         let cx = &cx;
2884//                         move |item| {
2885//                             let item = item.to_followable_item_handle(cx)?;
2886//                             if (project_id.is_none() || project_id != follower_project_id)
2887//                                 && item.is_project_item(cx)
2888//                             {
2889//                                 return None;
2890//                             }
2891//                             let id = item.remote_id(client, cx)?.to_proto();
2892//                             let variant = item.to_state_proto(cx)?;
2893//                             Some(proto::View {
2894//                                 id: Some(id),
2895//                                 leader_id,
2896//                                 variant: Some(variant),
2897//                             })
2898//                         }
2899//                     })
2900//                 })
2901//                 .collect(),
2902//         }
2903//     }
2904
2905//     fn handle_update_followers(
2906//         &mut self,
2907//         leader_id: PeerId,
2908//         message: proto::UpdateFollowers,
2909//         _cx: &mut ViewContext<Self>,
2910//     ) {
2911//         self.leader_updates_tx
2912//             .unbounded_send((leader_id, message))
2913//             .ok();
2914//     }
2915
2916//     async fn process_leader_update(
2917//         this: &WeakViewHandle<Self>,
2918//         leader_id: PeerId,
2919//         update: proto::UpdateFollowers,
2920//         cx: &mut AsyncAppContext,
2921//     ) -> Result<()> {
2922//         match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
2923//             proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
2924//                 this.update(cx, |this, _| {
2925//                     for (_, state) in &mut this.follower_states {
2926//                         if state.leader_id == leader_id {
2927//                             state.active_view_id =
2928//                                 if let Some(active_view_id) = update_active_view.id.clone() {
2929//                                     Some(ViewId::from_proto(active_view_id)?)
2930//                                 } else {
2931//                                     None
2932//                                 };
2933//                         }
2934//                     }
2935//                     anyhow::Ok(())
2936//                 })??;
2937//             }
2938//             proto::update_followers::Variant::UpdateView(update_view) => {
2939//                 let variant = update_view
2940//                     .variant
2941//                     .ok_or_else(|| anyhow!("missing update view variant"))?;
2942//                 let id = update_view
2943//                     .id
2944//                     .ok_or_else(|| anyhow!("missing update view id"))?;
2945//                 let mut tasks = Vec::new();
2946//                 this.update(cx, |this, cx| {
2947//                     let project = this.project.clone();
2948//                     for (_, state) in &mut this.follower_states {
2949//                         if state.leader_id == leader_id {
2950//                             let view_id = ViewId::from_proto(id.clone())?;
2951//                             if let Some(item) = state.items_by_leader_view_id.get(&view_id) {
2952//                                 tasks.push(item.apply_update_proto(&project, variant.clone(), cx));
2953//                             }
2954//                         }
2955//                     }
2956//                     anyhow::Ok(())
2957//                 })??;
2958//                 try_join_all(tasks).await.log_err();
2959//             }
2960//             proto::update_followers::Variant::CreateView(view) => {
2961//                 let panes = this.read_with(cx, |this, _| {
2962//                     this.follower_states
2963//                         .iter()
2964//                         .filter_map(|(pane, state)| (state.leader_id == leader_id).then_some(pane))
2965//                         .cloned()
2966//                         .collect()
2967//                 })?;
2968//                 Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?;
2969//             }
2970//         }
2971//         this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?;
2972//         Ok(())
2973//     }
2974
2975//     async fn add_views_from_leader(
2976//         this: WeakViewHandle<Self>,
2977//         leader_id: PeerId,
2978//         panes: Vec<ViewHandle<Pane>>,
2979//         views: Vec<proto::View>,
2980//         cx: &mut AsyncAppContext,
2981//     ) -> Result<()> {
2982//         let this = this
2983//             .upgrade(cx)
2984//             .ok_or_else(|| anyhow!("workspace dropped"))?;
2985
2986//         let item_builders = cx.update(|cx| {
2987//             cx.default_global::<FollowableItemBuilders>()
2988//                 .values()
2989//                 .map(|b| b.0)
2990//                 .collect::<Vec<_>>()
2991//         });
2992
2993//         let mut item_tasks_by_pane = HashMap::default();
2994//         for pane in panes {
2995//             let mut item_tasks = Vec::new();
2996//             let mut leader_view_ids = Vec::new();
2997//             for view in &views {
2998//                 let Some(id) = &view.id else { continue };
2999//                 let id = ViewId::from_proto(id.clone())?;
3000//                 let mut variant = view.variant.clone();
3001//                 if variant.is_none() {
3002//                     Err(anyhow!("missing view variant"))?;
3003//                 }
3004//                 for build_item in &item_builders {
3005//                     let task = cx
3006//                         .update(|cx| build_item(pane.clone(), this.clone(), id, &mut variant, cx));
3007//                     if let Some(task) = task {
3008//                         item_tasks.push(task);
3009//                         leader_view_ids.push(id);
3010//                         break;
3011//                     } else {
3012//                         assert!(variant.is_some());
3013//                     }
3014//                 }
3015//             }
3016
3017//             item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids));
3018//         }
3019
3020//         for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane {
3021//             let items = futures::future::try_join_all(item_tasks).await?;
3022//             this.update(cx, |this, cx| {
3023//                 let state = this.follower_states.get_mut(&pane)?;
3024//                 for (id, item) in leader_view_ids.into_iter().zip(items) {
3025//                     item.set_leader_peer_id(Some(leader_id), cx);
3026//                     state.items_by_leader_view_id.insert(id, item);
3027//                 }
3028
3029//                 Some(())
3030//             });
3031//         }
3032//         Ok(())
3033//     }
3034
3035//     fn update_active_view_for_followers(&mut self, cx: &AppContext) {
3036//         let mut is_project_item = true;
3037//         let mut update = proto::UpdateActiveView::default();
3038//         if self.active_pane.read(cx).has_focus() {
3039//             let item = self
3040//                 .active_item(cx)
3041//                 .and_then(|item| item.to_followable_item_handle(cx));
3042//             if let Some(item) = item {
3043//                 is_project_item = item.is_project_item(cx);
3044//                 update = proto::UpdateActiveView {
3045//                     id: item
3046//                         .remote_id(&self.app_state.client, cx)
3047//                         .map(|id| id.to_proto()),
3048//                     leader_id: self.leader_for_pane(&self.active_pane),
3049//                 };
3050//             }
3051//         }
3052
3053//         if update.id != self.last_active_view_id {
3054//             self.last_active_view_id = update.id.clone();
3055//             self.update_followers(
3056//                 is_project_item,
3057//                 proto::update_followers::Variant::UpdateActiveView(update),
3058//                 cx,
3059//             );
3060//         }
3061//     }
3062
3063//     fn update_followers(
3064//         &self,
3065//         project_only: bool,
3066//         update: proto::update_followers::Variant,
3067//         cx: &AppContext,
3068//     ) -> Option<()> {
3069//         let project_id = if project_only {
3070//             self.project.read(cx).remote_id()
3071//         } else {
3072//             None
3073//         };
3074//         self.app_state().workspace_store.read_with(cx, |store, cx| {
3075//             store.update_followers(project_id, update, cx)
3076//         })
3077//     }
3078
3079//     pub fn leader_for_pane(&self, pane: &ViewHandle<Pane>) -> Option<PeerId> {
3080//         self.follower_states.get(pane).map(|state| state.leader_id)
3081//     }
3082
3083//     fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
3084//         cx.notify();
3085
3086//         let call = self.active_call()?;
3087//         let room = call.read(cx).room()?.read(cx);
3088//         let participant = room.remote_participant_for_peer_id(leader_id)?;
3089//         let mut items_to_activate = Vec::new();
3090
3091//         let leader_in_this_app;
3092//         let leader_in_this_project;
3093//         match participant.location {
3094//             call::ParticipantLocation::SharedProject { project_id } => {
3095//                 leader_in_this_app = true;
3096//                 leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id();
3097//             }
3098//             call::ParticipantLocation::UnsharedProject => {
3099//                 leader_in_this_app = true;
3100//                 leader_in_this_project = false;
3101//             }
3102//             call::ParticipantLocation::External => {
3103//                 leader_in_this_app = false;
3104//                 leader_in_this_project = false;
3105//             }
3106//         };
3107
3108//         for (pane, state) in &self.follower_states {
3109//             if state.leader_id != leader_id {
3110//                 continue;
3111//             }
3112//             if let (Some(active_view_id), true) = (state.active_view_id, leader_in_this_app) {
3113//                 if let Some(item) = state.items_by_leader_view_id.get(&active_view_id) {
3114//                     if leader_in_this_project || !item.is_project_item(cx) {
3115//                         items_to_activate.push((pane.clone(), item.boxed_clone()));
3116//                     }
3117//                 } else {
3118//                     log::warn!(
3119//                         "unknown view id {:?} for leader {:?}",
3120//                         active_view_id,
3121//                         leader_id
3122//                     );
3123//                 }
3124//                 continue;
3125//             }
3126//             if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) {
3127//                 items_to_activate.push((pane.clone(), Box::new(shared_screen)));
3128//             }
3129//         }
3130
3131//         for (pane, item) in items_to_activate {
3132//             let pane_was_focused = pane.read(cx).has_focus();
3133//             if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) {
3134//                 pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx));
3135//             } else {
3136//                 pane.update(cx, |pane, cx| {
3137//                     pane.add_item(item.boxed_clone(), false, false, None, cx)
3138//                 });
3139//             }
3140
3141//             if pane_was_focused {
3142//                 pane.update(cx, |pane, cx| pane.focus_active_item(cx));
3143//             }
3144//         }
3145
3146//         None
3147//     }
3148
3149//     fn shared_screen_for_peer(
3150//         &self,
3151//         peer_id: PeerId,
3152//         pane: &ViewHandle<Pane>,
3153//         cx: &mut ViewContext<Self>,
3154//     ) -> Option<ViewHandle<SharedScreen>> {
3155//         let call = self.active_call()?;
3156//         let room = call.read(cx).room()?.read(cx);
3157//         let participant = room.remote_participant_for_peer_id(peer_id)?;
3158//         let track = participant.video_tracks.values().next()?.clone();
3159//         let user = participant.user.clone();
3160
3161//         for item in pane.read(cx).items_of_type::<SharedScreen>() {
3162//             if item.read(cx).peer_id == peer_id {
3163//                 return Some(item);
3164//             }
3165//         }
3166
3167//         Some(cx.add_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx)))
3168//     }
3169
3170//     pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
3171//         if active {
3172//             self.update_active_view_for_followers(cx);
3173//             cx.background()
3174//                 .spawn(persistence::DB.update_timestamp(self.database_id()))
3175//                 .detach();
3176//         } else {
3177//             for pane in &self.panes {
3178//                 pane.update(cx, |pane, cx| {
3179//                     if let Some(item) = pane.active_item() {
3180//                         item.workspace_deactivated(cx);
3181//                     }
3182//                     if matches!(
3183//                         settings::get::<WorkspaceSettings>(cx).autosave,
3184//                         AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange
3185//                     ) {
3186//                         for item in pane.items() {
3187//                             Pane::autosave_item(item.as_ref(), self.project.clone(), cx)
3188//                                 .detach_and_log_err(cx);
3189//                         }
3190//                     }
3191//                 });
3192//             }
3193//         }
3194//     }
3195
3196//     fn active_call(&self) -> Option<&ModelHandle<ActiveCall>> {
3197//         self.active_call.as_ref().map(|(call, _)| call)
3198//     }
3199
3200//     fn on_active_call_event(
3201//         &mut self,
3202//         _: ModelHandle<ActiveCall>,
3203//         event: &call::room::Event,
3204//         cx: &mut ViewContext<Self>,
3205//     ) {
3206//         match event {
3207//             call::room::Event::ParticipantLocationChanged { participant_id }
3208//             | call::room::Event::RemoteVideoTracksChanged { participant_id } => {
3209//                 self.leader_updated(*participant_id, cx);
3210//             }
3211//             _ => {}
3212//         }
3213//     }
3214
3215//     pub fn database_id(&self) -> WorkspaceId {
3216//         self.database_id
3217//     }
3218
3219//     fn location(&self, cx: &AppContext) -> Option<WorkspaceLocation> {
3220//         let project = self.project().read(cx);
3221
3222//         if project.is_local() {
3223//             Some(
3224//                 project
3225//                     .visible_worktrees(cx)
3226//                     .map(|worktree| worktree.read(cx).abs_path())
3227//                     .collect::<Vec<_>>()
3228//                     .into(),
3229//             )
3230//         } else {
3231//             None
3232//         }
3233//     }
3234
3235//     fn remove_panes(&mut self, member: Member, cx: &mut ViewContext<Workspace>) {
3236//         match member {
3237//             Member::Axis(PaneAxis { members, .. }) => {
3238//                 for child in members.iter() {
3239//                     self.remove_panes(child.clone(), cx)
3240//                 }
3241//             }
3242//             Member::Pane(pane) => {
3243//                 self.force_remove_pane(&pane, cx);
3244//             }
3245//         }
3246//     }
3247
3248//     fn force_remove_pane(&mut self, pane: &ViewHandle<Pane>, cx: &mut ViewContext<Workspace>) {
3249//         self.panes.retain(|p| p != pane);
3250//         cx.focus(self.panes.last().unwrap());
3251//         if self.last_active_center_pane == Some(pane.downgrade()) {
3252//             self.last_active_center_pane = None;
3253//         }
3254//         cx.notify();
3255//     }
3256
3257//     fn schedule_serialize(&mut self, cx: &mut ViewContext<Self>) {
3258//         self._schedule_serialize = Some(cx.spawn(|this, cx| async move {
3259//             cx.background().timer(Duration::from_millis(100)).await;
3260//             this.read_with(&cx, |this, cx| this.serialize_workspace(cx))
3261//                 .ok();
3262//         }));
3263//     }
3264
3265//     fn serialize_workspace(&self, cx: &ViewContext<Self>) {
3266//         fn serialize_pane_handle(
3267//             pane_handle: &ViewHandle<Pane>,
3268//             cx: &AppContext,
3269//         ) -> SerializedPane {
3270//             let (items, active) = {
3271//                 let pane = pane_handle.read(cx);
3272//                 let active_item_id = pane.active_item().map(|item| item.id());
3273//                 (
3274//                     pane.items()
3275//                         .filter_map(|item_handle| {
3276//                             Some(SerializedItem {
3277//                                 kind: Arc::from(item_handle.serialized_item_kind()?),
3278//                                 item_id: item_handle.id(),
3279//                                 active: Some(item_handle.id()) == active_item_id,
3280//                             })
3281//                         })
3282//                         .collect::<Vec<_>>(),
3283//                     pane.has_focus(),
3284//                 )
3285//             };
3286
3287//             SerializedPane::new(items, active)
3288//         }
3289
3290//         fn build_serialized_pane_group(
3291//             pane_group: &Member,
3292//             cx: &AppContext,
3293//         ) -> SerializedPaneGroup {
3294//             match pane_group {
3295//                 Member::Axis(PaneAxis {
3296//                     axis,
3297//                     members,
3298//                     flexes,
3299//                     bounding_boxes: _,
3300//                 }) => SerializedPaneGroup::Group {
3301//                     axis: *axis,
3302//                     children: members
3303//                         .iter()
3304//                         .map(|member| build_serialized_pane_group(member, cx))
3305//                         .collect::<Vec<_>>(),
3306//                     flexes: Some(flexes.borrow().clone()),
3307//                 },
3308//                 Member::Pane(pane_handle) => {
3309//                     SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
3310//                 }
3311//             }
3312//         }
3313
3314//         fn build_serialized_docks(this: &Workspace, cx: &ViewContext<Workspace>) -> DockStructure {
3315//             let left_dock = this.left_dock.read(cx);
3316//             let left_visible = left_dock.is_open();
3317//             let left_active_panel = left_dock.visible_panel().and_then(|panel| {
3318//                 Some(
3319//                     cx.view_ui_name(panel.as_any().window(), panel.id())?
3320//                         .to_string(),
3321//                 )
3322//             });
3323//             let left_dock_zoom = left_dock
3324//                 .visible_panel()
3325//                 .map(|panel| panel.is_zoomed(cx))
3326//                 .unwrap_or(false);
3327
3328//             let right_dock = this.right_dock.read(cx);
3329//             let right_visible = right_dock.is_open();
3330//             let right_active_panel = right_dock.visible_panel().and_then(|panel| {
3331//                 Some(
3332//                     cx.view_ui_name(panel.as_any().window(), panel.id())?
3333//                         .to_string(),
3334//                 )
3335//             });
3336//             let right_dock_zoom = right_dock
3337//                 .visible_panel()
3338//                 .map(|panel| panel.is_zoomed(cx))
3339//                 .unwrap_or(false);
3340
3341//             let bottom_dock = this.bottom_dock.read(cx);
3342//             let bottom_visible = bottom_dock.is_open();
3343//             let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| {
3344//                 Some(
3345//                     cx.view_ui_name(panel.as_any().window(), panel.id())?
3346//                         .to_string(),
3347//                 )
3348//             });
3349//             let bottom_dock_zoom = bottom_dock
3350//                 .visible_panel()
3351//                 .map(|panel| panel.is_zoomed(cx))
3352//                 .unwrap_or(false);
3353
3354//             DockStructure {
3355//                 left: DockData {
3356//                     visible: left_visible,
3357//                     active_panel: left_active_panel,
3358//                     zoom: left_dock_zoom,
3359//                 },
3360//                 right: DockData {
3361//                     visible: right_visible,
3362//                     active_panel: right_active_panel,
3363//                     zoom: right_dock_zoom,
3364//                 },
3365//                 bottom: DockData {
3366//                     visible: bottom_visible,
3367//                     active_panel: bottom_active_panel,
3368//                     zoom: bottom_dock_zoom,
3369//                 },
3370//             }
3371//         }
3372
3373//         if let Some(location) = self.location(cx) {
3374//             // Load bearing special case:
3375//             //  - with_local_workspace() relies on this to not have other stuff open
3376//             //    when you open your log
3377//             if !location.paths().is_empty() {
3378//                 let center_group = build_serialized_pane_group(&self.center.root, cx);
3379//                 let docks = build_serialized_docks(self, cx);
3380
3381//                 let serialized_workspace = SerializedWorkspace {
3382//                     id: self.database_id,
3383//                     location,
3384//                     center_group,
3385//                     bounds: Default::default(),
3386//                     display: Default::default(),
3387//                     docks,
3388//                 };
3389
3390//                 cx.background()
3391//                     .spawn(persistence::DB.save_workspace(serialized_workspace))
3392//                     .detach();
3393//             }
3394//         }
3395//     }
3396
3397//     pub(crate) fn load_workspace(
3398//         workspace: WeakViewHandle<Workspace>,
3399//         serialized_workspace: SerializedWorkspace,
3400//         paths_to_open: Vec<Option<ProjectPath>>,
3401//         cx: &mut AppContext,
3402//     ) -> Task<Result<Vec<Option<Box<dyn ItemHandle>>>>> {
3403//         cx.spawn(|mut cx| async move {
3404//             let (project, old_center_pane) = workspace.read_with(&cx, |workspace, _| {
3405//                 (
3406//                     workspace.project().clone(),
3407//                     workspace.last_active_center_pane.clone(),
3408//                 )
3409//             })?;
3410
3411//             let mut center_group = None;
3412//             let mut center_items = None;
3413//             // Traverse the splits tree and add to things
3414//             if let Some((group, active_pane, items)) = serialized_workspace
3415//                 .center_group
3416//                 .deserialize(&project, serialized_workspace.id, &workspace, &mut cx)
3417//                 .await
3418//             {
3419//                 center_items = Some(items);
3420//                 center_group = Some((group, active_pane))
3421//             }
3422
3423//             let mut items_by_project_path = cx.read(|cx| {
3424//                 center_items
3425//                     .unwrap_or_default()
3426//                     .into_iter()
3427//                     .filter_map(|item| {
3428//                         let item = item?;
3429//                         let project_path = item.project_path(cx)?;
3430//                         Some((project_path, item))
3431//                     })
3432//                     .collect::<HashMap<_, _>>()
3433//             });
3434
3435//             let opened_items = paths_to_open
3436//                 .into_iter()
3437//                 .map(|path_to_open| {
3438//                     path_to_open
3439//                         .and_then(|path_to_open| items_by_project_path.remove(&path_to_open))
3440//                 })
3441//                 .collect::<Vec<_>>();
3442
3443//             // Remove old panes from workspace panes list
3444//             workspace.update(&mut cx, |workspace, cx| {
3445//                 if let Some((center_group, active_pane)) = center_group {
3446//                     workspace.remove_panes(workspace.center.root.clone(), cx);
3447
3448//                     // Swap workspace center group
3449//                     workspace.center = PaneGroup::with_root(center_group);
3450
3451//                     // Change the focus to the workspace first so that we retrigger focus in on the pane.
3452//                     cx.focus_self();
3453
3454//                     if let Some(active_pane) = active_pane {
3455//                         cx.focus(&active_pane);
3456//                     } else {
3457//                         cx.focus(workspace.panes.last().unwrap());
3458//                     }
3459//                 } else {
3460//                     let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx));
3461//                     if let Some(old_center_handle) = old_center_handle {
3462//                         cx.focus(&old_center_handle)
3463//                     } else {
3464//                         cx.focus_self()
3465//                     }
3466//                 }
3467
3468//                 let docks = serialized_workspace.docks;
3469//                 workspace.left_dock.update(cx, |dock, cx| {
3470//                     dock.set_open(docks.left.visible, cx);
3471//                     if let Some(active_panel) = docks.left.active_panel {
3472//                         if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3473//                             dock.activate_panel(ix, cx);
3474//                         }
3475//                     }
3476//                     dock.active_panel()
3477//                         .map(|panel| panel.set_zoomed(docks.left.zoom, cx));
3478//                     if docks.left.visible && docks.left.zoom {
3479//                         cx.focus_self()
3480//                     }
3481//                 });
3482//                 // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
3483//                 workspace.right_dock.update(cx, |dock, cx| {
3484//                     dock.set_open(docks.right.visible, cx);
3485//                     if let Some(active_panel) = docks.right.active_panel {
3486//                         if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3487//                             dock.activate_panel(ix, cx);
3488//                         }
3489//                     }
3490//                     dock.active_panel()
3491//                         .map(|panel| panel.set_zoomed(docks.right.zoom, cx));
3492
3493//                     if docks.right.visible && docks.right.zoom {
3494//                         cx.focus_self()
3495//                     }
3496//                 });
3497//                 workspace.bottom_dock.update(cx, |dock, cx| {
3498//                     dock.set_open(docks.bottom.visible, cx);
3499//                     if let Some(active_panel) = docks.bottom.active_panel {
3500//                         if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3501//                             dock.activate_panel(ix, cx);
3502//                         }
3503//                     }
3504
3505//                     dock.active_panel()
3506//                         .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx));
3507
3508//                     if docks.bottom.visible && docks.bottom.zoom {
3509//                         cx.focus_self()
3510//                     }
3511//                 });
3512
3513//                 cx.notify();
3514//             })?;
3515
3516//             // Serialize ourself to make sure our timestamps and any pane / item changes are replicated
3517//             workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))?;
3518
3519//             Ok(opened_items)
3520//         })
3521//     }
3522
3523//     #[cfg(any(test, feature = "test-support"))]
3524//     pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
3525//         use node_runtime::FakeNodeRuntime;
3526
3527//         let client = project.read(cx).client();
3528//         let user_store = project.read(cx).user_store();
3529
3530//         let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
3531//         let app_state = Arc::new(AppState {
3532//             languages: project.read(cx).languages().clone(),
3533//             workspace_store,
3534//             client,
3535//             user_store,
3536//             fs: project.read(cx).fs().clone(),
3537//             build_window_options: |_, _, _| Default::default(),
3538//             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
3539//             background_actions: || &[],
3540//             node_runtime: FakeNodeRuntime::new(),
3541//         });
3542//         Self::new(0, project, app_state, cx)
3543//     }
3544
3545//     fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
3546//         let dock = match position {
3547//             DockPosition::Left => &self.left_dock,
3548//             DockPosition::Right => &self.right_dock,
3549//             DockPosition::Bottom => &self.bottom_dock,
3550//         };
3551//         let active_panel = dock.read(cx).visible_panel()?;
3552//         let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) {
3553//             dock.read(cx).render_placeholder(cx)
3554//         } else {
3555//             ChildView::new(dock, cx).into_any()
3556//         };
3557
3558//         Some(
3559//             element
3560//                 .constrained()
3561//                 .dynamically(move |constraint, _, cx| match position {
3562//                     DockPosition::Left | DockPosition::Right => SizeConstraint::new(
3563//                         Vector2F::new(20., constraint.min.y()),
3564//                         Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()),
3565//                     ),
3566//                     DockPosition::Bottom => SizeConstraint::new(
3567//                         Vector2F::new(constraint.min.x(), 20.),
3568//                         Vector2F::new(constraint.max.x(), cx.window_size().y() * 0.8),
3569//                     ),
3570//                 })
3571//                 .into_any(),
3572//         )
3573//     }
3574// }
3575
3576// fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
3577//     ZED_WINDOW_POSITION
3578//         .zip(*ZED_WINDOW_SIZE)
3579//         .map(|(position, size)| {
3580//             WindowBounds::Fixed(RectF::new(
3581//                 cx.platform().screens()[0].bounds().origin() + position,
3582//                 size,
3583//             ))
3584//         })
3585// }
3586
3587// async fn open_items(
3588//     serialized_workspace: Option<SerializedWorkspace>,
3589//     workspace: &WeakViewHandle<Workspace>,
3590//     mut project_paths_to_open: Vec<(PathBuf, Option<ProjectPath>)>,
3591//     app_state: Arc<AppState>,
3592//     mut cx: AsyncAppContext,
3593// ) -> Result<Vec<Option<Result<Box<dyn ItemHandle>>>>> {
3594//     let mut opened_items = Vec::with_capacity(project_paths_to_open.len());
3595
3596//     if let Some(serialized_workspace) = serialized_workspace {
3597//         let workspace = workspace.clone();
3598//         let restored_items = cx
3599//             .update(|cx| {
3600//                 Workspace::load_workspace(
3601//                     workspace,
3602//                     serialized_workspace,
3603//                     project_paths_to_open
3604//                         .iter()
3605//                         .map(|(_, project_path)| project_path)
3606//                         .cloned()
3607//                         .collect(),
3608//                     cx,
3609//                 )
3610//             })
3611//             .await?;
3612
3613//         let restored_project_paths = cx.read(|cx| {
3614//             restored_items
3615//                 .iter()
3616//                 .filter_map(|item| item.as_ref()?.project_path(cx))
3617//                 .collect::<HashSet<_>>()
3618//         });
3619
3620//         for restored_item in restored_items {
3621//             opened_items.push(restored_item.map(Ok));
3622//         }
3623
3624//         project_paths_to_open
3625//             .iter_mut()
3626//             .for_each(|(_, project_path)| {
3627//                 if let Some(project_path_to_open) = project_path {
3628//                     if restored_project_paths.contains(project_path_to_open) {
3629//                         *project_path = None;
3630//                     }
3631//                 }
3632//             });
3633//     } else {
3634//         for _ in 0..project_paths_to_open.len() {
3635//             opened_items.push(None);
3636//         }
3637//     }
3638//     assert!(opened_items.len() == project_paths_to_open.len());
3639
3640//     let tasks =
3641//         project_paths_to_open
3642//             .into_iter()
3643//             .enumerate()
3644//             .map(|(i, (abs_path, project_path))| {
3645//                 let workspace = workspace.clone();
3646//                 cx.spawn(|mut cx| {
3647//                     let fs = app_state.fs.clone();
3648//                     async move {
3649//                         let file_project_path = project_path?;
3650//                         if fs.is_file(&abs_path).await {
3651//                             Some((
3652//                                 i,
3653//                                 workspace
3654//                                     .update(&mut cx, |workspace, cx| {
3655//                                         workspace.open_path(file_project_path, None, true, cx)
3656//                                     })
3657//                                     .log_err()?
3658//                                     .await,
3659//                             ))
3660//                         } else {
3661//                             None
3662//                         }
3663//                     }
3664//                 })
3665//             });
3666
3667//     for maybe_opened_path in futures::future::join_all(tasks.into_iter())
3668//         .await
3669//         .into_iter()
3670//     {
3671//         if let Some((i, path_open_result)) = maybe_opened_path {
3672//             opened_items[i] = Some(path_open_result);
3673//         }
3674//     }
3675
3676//     Ok(opened_items)
3677// }
3678
3679// fn notify_of_new_dock(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppContext) {
3680//     const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
3681//     const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
3682//     const MESSAGE_ID: usize = 2;
3683
3684//     if workspace
3685//         .read_with(cx, |workspace, cx| {
3686//             workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
3687//         })
3688//         .unwrap_or(false)
3689//     {
3690//         return;
3691//     }
3692
3693//     if db::kvp::KEY_VALUE_STORE
3694//         .read_kvp(NEW_DOCK_HINT_KEY)
3695//         .ok()
3696//         .flatten()
3697//         .is_some()
3698//     {
3699//         if !workspace
3700//             .read_with(cx, |workspace, cx| {
3701//                 workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
3702//             })
3703//             .unwrap_or(false)
3704//         {
3705//             cx.update(|cx| {
3706//                 cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
3707//                     let entry = tracker
3708//                         .entry(TypeId::of::<MessageNotification>())
3709//                         .or_default();
3710//                     if !entry.contains(&MESSAGE_ID) {
3711//                         entry.push(MESSAGE_ID);
3712//                     }
3713//                 });
3714//             });
3715//         }
3716
3717//         return;
3718//     }
3719
3720//     cx.spawn(|_| async move {
3721//         db::kvp::KEY_VALUE_STORE
3722//             .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
3723//             .await
3724//             .ok();
3725//     })
3726//     .detach();
3727
3728//     workspace
3729//         .update(cx, |workspace, cx| {
3730//             workspace.show_notification_once(2, cx, |cx| {
3731//                 cx.add_view(|_| {
3732//                     MessageNotification::new_element(|text, _| {
3733//                         Text::new(
3734//                             "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
3735//                             text,
3736//                         )
3737//                         .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
3738//                             let code_span_background_color = settings::get::<ThemeSettings>(cx)
3739//                                 .theme
3740//                                 .editor
3741//                                 .document_highlight_read_background;
3742
3743//                             cx.scene().push_quad(gpui::Quad {
3744//                                 bounds,
3745//                                 background: Some(code_span_background_color),
3746//                                 border: Default::default(),
3747//                                 corner_radii: (2.0).into(),
3748//                             })
3749//                         })
3750//                         .into_any()
3751//                     })
3752//                     .with_click_message("Read more about the new panel system")
3753//                     .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
3754//                 })
3755//             })
3756//         })
3757//         .ok();
3758// }
3759
3760// fn notify_if_database_failed(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppContext) {
3761//     const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
3762
3763//     workspace
3764//         .update(cx, |workspace, cx| {
3765//             if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
3766//                 workspace.show_notification_once(0, cx, |cx| {
3767//                     cx.add_view(|_| {
3768//                         MessageNotification::new("Failed to load the database file.")
3769//                             .with_click_message("Click to let us know about this error")
3770//                             .on_click(|cx| cx.platform().open_url(REPORT_ISSUE_URL))
3771//                     })
3772//                 });
3773//             }
3774//         })
3775//         .log_err();
3776// }
3777
3778// impl Entity for Workspace {
3779//     type Event = Event;
3780
3781//     fn release(&mut self, cx: &mut AppContext) {
3782//         self.app_state.workspace_store.update(cx, |store, _| {
3783//             store.workspaces.remove(&self.weak_self);
3784//         })
3785//     }
3786// }
3787
3788// impl View for Workspace {
3789//     fn ui_name() -> &'static str {
3790//         "Workspace"
3791//     }
3792
3793//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
3794//         let theme = theme::current(cx).clone();
3795//         Stack::new()
3796//             .with_child(
3797//                 Flex::column()
3798//                     .with_child(self.render_titlebar(&theme, cx))
3799//                     .with_child(
3800//                         Stack::new()
3801//                             .with_child({
3802//                                 let project = self.project.clone();
3803//                                 Flex::row()
3804//                                     .with_children(self.render_dock(DockPosition::Left, cx))
3805//                                     .with_child(
3806//                                         Flex::column()
3807//                                             .with_child(
3808//                                                 FlexItem::new(
3809//                                                     self.center.render(
3810//                                                         &project,
3811//                                                         &theme,
3812//                                                         &self.follower_states,
3813//                                                         self.active_call(),
3814//                                                         self.active_pane(),
3815//                                                         self.zoomed
3816//                                                             .as_ref()
3817//                                                             .and_then(|zoomed| zoomed.upgrade(cx))
3818//                                                             .as_ref(),
3819//                                                         &self.app_state,
3820//                                                         cx,
3821//                                                     ),
3822//                                                 )
3823//                                                 .flex(1., true),
3824//                                             )
3825//                                             .with_children(
3826//                                                 self.render_dock(DockPosition::Bottom, cx),
3827//                                             )
3828//                                             .flex(1., true),
3829//                                     )
3830//                                     .with_children(self.render_dock(DockPosition::Right, cx))
3831//                             })
3832//                             .with_child(Overlay::new(
3833//                                 Stack::new()
3834//                                     .with_children(self.zoomed.as_ref().and_then(|zoomed| {
3835//                                         enum ZoomBackground {}
3836//                                         let zoomed = zoomed.upgrade(cx)?;
3837
3838//                                         let mut foreground_style =
3839//                                             theme.workspace.zoomed_pane_foreground;
3840//                                         if let Some(zoomed_dock_position) = self.zoomed_position {
3841//                                             foreground_style =
3842//                                                 theme.workspace.zoomed_panel_foreground;
3843//                                             let margin = foreground_style.margin.top;
3844//                                             let border = foreground_style.border.top;
3845
3846//                                             // Only include a margin and border on the opposite side.
3847//                                             foreground_style.margin.top = 0.;
3848//                                             foreground_style.margin.left = 0.;
3849//                                             foreground_style.margin.bottom = 0.;
3850//                                             foreground_style.margin.right = 0.;
3851//                                             foreground_style.border.top = false;
3852//                                             foreground_style.border.left = false;
3853//                                             foreground_style.border.bottom = false;
3854//                                             foreground_style.border.right = false;
3855//                                             match zoomed_dock_position {
3856//                                                 DockPosition::Left => {
3857//                                                     foreground_style.margin.right = margin;
3858//                                                     foreground_style.border.right = border;
3859//                                                 }
3860//                                                 DockPosition::Right => {
3861//                                                     foreground_style.margin.left = margin;
3862//                                                     foreground_style.border.left = border;
3863//                                                 }
3864//                                                 DockPosition::Bottom => {
3865//                                                     foreground_style.margin.top = margin;
3866//                                                     foreground_style.border.top = border;
3867//                                                 }
3868//                                             }
3869//                                         }
3870
3871//                                         Some(
3872//                                             ChildView::new(&zoomed, cx)
3873//                                                 .contained()
3874//                                                 .with_style(foreground_style)
3875//                                                 .aligned()
3876//                                                 .contained()
3877//                                                 .with_style(theme.workspace.zoomed_background)
3878//                                                 .mouse::<ZoomBackground>(0)
3879//                                                 .capture_all()
3880//                                                 .on_down(
3881//                                                     MouseButton::Left,
3882//                                                     |_, this: &mut Self, cx| {
3883//                                                         this.zoom_out(cx);
3884//                                                     },
3885//                                                 ),
3886//                                         )
3887//                                     }))
3888//                                     .with_children(self.modal.as_ref().map(|modal| {
3889//                                         // Prevent clicks within the modal from falling
3890//                                         // through to the rest of the workspace.
3891//                                         enum ModalBackground {}
3892//                                         MouseEventHandler::new::<ModalBackground, _>(
3893//                                             0,
3894//                                             cx,
3895//                                             |_, cx| ChildView::new(modal.view.as_any(), cx),
3896//                                         )
3897//                                         .on_click(MouseButton::Left, |_, _, _| {})
3898//                                         .contained()
3899//                                         .with_style(theme.workspace.modal)
3900//                                         .aligned()
3901//                                         .top()
3902//                                     }))
3903//                                     .with_children(self.render_notifications(&theme.workspace, cx)),
3904//                             ))
3905//                             .provide_resize_bounds::<WorkspaceBounds>()
3906//                             .flex(1.0, true),
3907//                     )
3908//                     .with_child(ChildView::new(&self.status_bar, cx))
3909//                     .contained()
3910//                     .with_background_color(theme.workspace.background),
3911//             )
3912//             .with_children(DragAndDrop::render(cx))
3913//             .with_children(self.render_disconnected_overlay(cx))
3914//             .into_any_named("workspace")
3915//     }
3916
3917//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
3918//         if cx.is_self_focused() {
3919//             cx.focus(&self.active_pane);
3920//         }
3921//     }
3922
3923//     fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
3924//         DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
3925//     }
3926// }
3927
3928// impl WorkspaceStore {
3929//     pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
3930//         Self {
3931//             workspaces: Default::default(),
3932//             followers: Default::default(),
3933//             _subscriptions: vec![
3934//                 client.add_request_handler(cx.handle(), Self::handle_follow),
3935//                 client.add_message_handler(cx.handle(), Self::handle_unfollow),
3936//                 client.add_message_handler(cx.handle(), Self::handle_update_followers),
3937//             ],
3938//             client,
3939//         }
3940//     }
3941
3942//     pub fn update_followers(
3943//         &self,
3944//         project_id: Option<u64>,
3945//         update: proto::update_followers::Variant,
3946//         cx: &AppContext,
3947//     ) -> Option<()> {
3948//         if !cx.has_global::<ModelHandle<ActiveCall>>() {
3949//             return None;
3950//         }
3951
3952//         let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id();
3953//         let follower_ids: Vec<_> = self
3954//             .followers
3955//             .iter()
3956//             .filter_map(|follower| {
3957//                 if follower.project_id == project_id || project_id.is_none() {
3958//                     Some(follower.peer_id.into())
3959//                 } else {
3960//                     None
3961//                 }
3962//             })
3963//             .collect();
3964//         if follower_ids.is_empty() {
3965//             return None;
3966//         }
3967//         self.client
3968//             .send(proto::UpdateFollowers {
3969//                 room_id,
3970//                 project_id,
3971//                 follower_ids,
3972//                 variant: Some(update),
3973//             })
3974//             .log_err()
3975//     }
3976
3977//     async fn handle_follow(
3978//         this: ModelHandle<Self>,
3979//         envelope: TypedEnvelope<proto::Follow>,
3980//         _: Arc<Client>,
3981//         mut cx: AsyncAppContext,
3982//     ) -> Result<proto::FollowResponse> {
3983//         this.update(&mut cx, |this, cx| {
3984//             let follower = Follower {
3985//                 project_id: envelope.payload.project_id,
3986//                 peer_id: envelope.original_sender_id()?,
3987//             };
3988//             let active_project = ActiveCall::global(cx)
3989//                 .read(cx)
3990//                 .location()
3991//                 .map(|project| project.id());
3992
3993//             let mut response = proto::FollowResponse::default();
3994//             for workspace in &this.workspaces {
3995//                 let Some(workspace) = workspace.upgrade(cx) else {
3996//                     continue;
3997//                 };
3998
3999//                 workspace.update(cx.as_mut(), |workspace, cx| {
4000//                     let handler_response = workspace.handle_follow(follower.project_id, cx);
4001//                     if response.views.is_empty() {
4002//                         response.views = handler_response.views;
4003//                     } else {
4004//                         response.views.extend_from_slice(&handler_response.views);
4005//                     }
4006
4007//                     if let Some(active_view_id) = handler_response.active_view_id.clone() {
4008//                         if response.active_view_id.is_none()
4009//                             || Some(workspace.project.id()) == active_project
4010//                         {
4011//                             response.active_view_id = Some(active_view_id);
4012//                         }
4013//                     }
4014//                 });
4015//             }
4016
4017//             if let Err(ix) = this.followers.binary_search(&follower) {
4018//                 this.followers.insert(ix, follower);
4019//             }
4020
4021//             Ok(response)
4022//         })
4023//     }
4024
4025//     async fn handle_unfollow(
4026//         this: ModelHandle<Self>,
4027//         envelope: TypedEnvelope<proto::Unfollow>,
4028//         _: Arc<Client>,
4029//         mut cx: AsyncAppContext,
4030//     ) -> Result<()> {
4031//         this.update(&mut cx, |this, _| {
4032//             let follower = Follower {
4033//                 project_id: envelope.payload.project_id,
4034//                 peer_id: envelope.original_sender_id()?,
4035//             };
4036//             if let Ok(ix) = this.followers.binary_search(&follower) {
4037//                 this.followers.remove(ix);
4038//             }
4039//             Ok(())
4040//         })
4041//     }
4042
4043//     async fn handle_update_followers(
4044//         this: ModelHandle<Self>,
4045//         envelope: TypedEnvelope<proto::UpdateFollowers>,
4046//         _: Arc<Client>,
4047//         mut cx: AsyncAppContext,
4048//     ) -> Result<()> {
4049//         let leader_id = envelope.original_sender_id()?;
4050//         let update = envelope.payload;
4051//         this.update(&mut cx, |this, cx| {
4052//             for workspace in &this.workspaces {
4053//                 let Some(workspace) = workspace.upgrade(cx) else {
4054//                     continue;
4055//                 };
4056//                 workspace.update(cx.as_mut(), |workspace, cx| {
4057//                     let project_id = workspace.project.read(cx).remote_id();
4058//                     if update.project_id != project_id && update.project_id.is_some() {
4059//                         return;
4060//                     }
4061//                     workspace.handle_update_followers(leader_id, update.clone(), cx);
4062//                 });
4063//             }
4064//             Ok(())
4065//         })
4066//     }
4067// }
4068
4069// impl Entity for WorkspaceStore {
4070//     type Event = ();
4071// }
4072
4073// impl ViewId {
4074//     pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
4075//         Ok(Self {
4076//             creator: message
4077//                 .creator
4078//                 .ok_or_else(|| anyhow!("creator is missing"))?,
4079//             id: message.id,
4080//         })
4081//     }
4082
4083//     pub(crate) fn to_proto(&self) -> proto::ViewId {
4084//         proto::ViewId {
4085//             creator: Some(self.creator),
4086//             id: self.id,
4087//         }
4088//     }
4089// }
4090
4091// pub trait WorkspaceHandle {
4092//     fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
4093// }
4094
4095// impl WorkspaceHandle for ViewHandle<Workspace> {
4096//     fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
4097//         self.read(cx)
4098//             .worktrees(cx)
4099//             .flat_map(|worktree| {
4100//                 let worktree_id = worktree.read(cx).id();
4101//                 worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
4102//                     worktree_id,
4103//                     path: f.path.clone(),
4104//                 })
4105//             })
4106//             .collect::<Vec<_>>()
4107//     }
4108// }
4109
4110// impl std::fmt::Debug for OpenPaths {
4111//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4112//         f.debug_struct("OpenPaths")
4113//             .field("paths", &self.paths)
4114//             .finish()
4115//     }
4116// }
4117
4118// pub struct WorkspaceCreated(pub WeakViewHandle<Workspace>);
4119
4120// pub fn activate_workspace_for_project(
4121//     cx: &mut AsyncAppContext,
4122//     predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
4123// ) -> Option<WeakViewHandle<Workspace>> {
4124//     for window in cx.windows() {
4125//         let handle = window
4126//             .update(cx, |cx| {
4127//                 if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
4128//                     let project = workspace_handle.read(cx).project.clone();
4129//                     if project.update(cx, &predicate) {
4130//                         cx.activate_window();
4131//                         return Some(workspace_handle.clone());
4132//                     }
4133//                 }
4134//                 None
4135//             })
4136//             .flatten();
4137
4138//         if let Some(handle) = handle {
4139//             return Some(handle.downgrade());
4140//         }
4141//     }
4142//     None
4143// }
4144
4145// pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
4146//     DB.last_workspace().await.log_err().flatten()
4147// }
4148
4149// async fn join_channel_internal(
4150//     channel_id: u64,
4151//     app_state: &Arc<AppState>,
4152//     requesting_window: Option<WindowHandle<Workspace>>,
4153//     active_call: &ModelHandle<ActiveCall>,
4154//     cx: &mut AsyncAppContext,
4155// ) -> Result<bool> {
4156//     let (should_prompt, open_room) = active_call.read_with(cx, |active_call, cx| {
4157//         let Some(room) = active_call.room().map(|room| room.read(cx)) else {
4158//             return (false, None);
4159//         };
4160
4161//         let already_in_channel = room.channel_id() == Some(channel_id);
4162//         let should_prompt = room.is_sharing_project()
4163//             && room.remote_participants().len() > 0
4164//             && !already_in_channel;
4165//         let open_room = if already_in_channel {
4166//             active_call.room().cloned()
4167//         } else {
4168//             None
4169//         };
4170//         (should_prompt, open_room)
4171//     });
4172
4173//     if let Some(room) = open_room {
4174//         let task = room.update(cx, |room, cx| {
4175//             if let Some((project, host)) = room.most_active_project(cx) {
4176//                 return Some(join_remote_project(project, host, app_state.clone(), cx));
4177//             }
4178
4179//             None
4180//         });
4181//         if let Some(task) = task {
4182//             task.await?;
4183//         }
4184//         return anyhow::Ok(true);
4185//     }
4186
4187//     if should_prompt {
4188//         if let Some(workspace) = requesting_window {
4189//             if let Some(window) = workspace.update(cx, |cx| cx.window()) {
4190//                 let answer = window.prompt(
4191//                     PromptLevel::Warning,
4192//                     "Leaving this call will unshare your current project.\nDo you want to switch channels?",
4193//                     &["Yes, Join Channel", "Cancel"],
4194//                     cx,
4195//                 );
4196
4197//                 if let Some(mut answer) = answer {
4198//                     if answer.next().await == Some(1) {
4199//                         return Ok(false);
4200//                     }
4201//                 }
4202//             } else {
4203//                 return Ok(false); // unreachable!() hopefully
4204//             }
4205//         } else {
4206//             return Ok(false); // unreachable!() hopefully
4207//         }
4208//     }
4209
4210//     let client = cx.read(|cx| active_call.read(cx).client());
4211
4212//     let mut client_status = client.status();
4213
4214//     // this loop will terminate within client::CONNECTION_TIMEOUT seconds.
4215//     'outer: loop {
4216//         let Some(status) = client_status.recv().await else {
4217//             return Err(anyhow!("error connecting"));
4218//         };
4219
4220//         match status {
4221//             Status::Connecting
4222//             | Status::Authenticating
4223//             | Status::Reconnecting
4224//             | Status::Reauthenticating => continue,
4225//             Status::Connected { .. } => break 'outer,
4226//             Status::SignedOut => return Err(anyhow!("not signed in")),
4227//             Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
4228//             Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
4229//                 return Err(anyhow!("zed is offline"))
4230//             }
4231//         }
4232//     }
4233
4234//     let room = active_call
4235//         .update(cx, |active_call, cx| {
4236//             active_call.join_channel(channel_id, cx)
4237//         })
4238//         .await?;
4239
4240//     room.update(cx, |room, _| room.room_update_completed())
4241//         .await;
4242
4243//     let task = room.update(cx, |room, cx| {
4244//         if let Some((project, host)) = room.most_active_project(cx) {
4245//             return Some(join_remote_project(project, host, app_state.clone(), cx));
4246//         }
4247
4248//         None
4249//     });
4250//     if let Some(task) = task {
4251//         task.await?;
4252//         return anyhow::Ok(true);
4253//     }
4254//     anyhow::Ok(false)
4255// }
4256
4257// pub fn join_channel(
4258//     channel_id: u64,
4259//     app_state: Arc<AppState>,
4260//     requesting_window: Option<WindowHandle<Workspace>>,
4261//     cx: &mut AppContext,
4262// ) -> Task<Result<()>> {
4263//     let active_call = ActiveCall::global(cx);
4264//     cx.spawn(|mut cx| async move {
4265//         let result = join_channel_internal(
4266//             channel_id,
4267//             &app_state,
4268//             requesting_window,
4269//             &active_call,
4270//             &mut cx,
4271//         )
4272//         .await;
4273
4274//         // join channel succeeded, and opened a window
4275//         if matches!(result, Ok(true)) {
4276//             return anyhow::Ok(());
4277//         }
4278
4279//         if requesting_window.is_some() {
4280//             return anyhow::Ok(());
4281//         }
4282
4283//         // find an existing workspace to focus and show call controls
4284//         let mut active_window = activate_any_workspace_window(&mut cx);
4285//         if active_window.is_none() {
4286//             // no open workspaces, make one to show the error in (blergh)
4287//             cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
4288//                 .await;
4289//         }
4290
4291//         active_window = activate_any_workspace_window(&mut cx);
4292//         if active_window.is_none() {
4293//             return result.map(|_| ()); // unreachable!() assuming new_local always opens a window
4294//         }
4295
4296//         if let Err(err) = result {
4297//             let prompt = active_window.unwrap().prompt(
4298//                 PromptLevel::Critical,
4299//                 &format!("Failed to join channel: {}", err),
4300//                 &["Ok"],
4301//                 &mut cx,
4302//             );
4303//             if let Some(mut prompt) = prompt {
4304//                 prompt.next().await;
4305//             } else {
4306//                 return Err(err);
4307//             }
4308//         }
4309
4310//         // return ok, we showed the error to the user.
4311//         return anyhow::Ok(());
4312//     })
4313// }
4314
4315// pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
4316//     for window in cx.windows() {
4317//         let found = window.update(cx, |cx| {
4318//             let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
4319//             if is_workspace {
4320//                 cx.activate_window();
4321//             }
4322//             is_workspace
4323//         });
4324//         if found == Some(true) {
4325//             return Some(window);
4326//         }
4327//     }
4328//     None
4329// }
4330
4331// #[allow(clippy::type_complexity)]
4332// pub fn open_paths(
4333//     abs_paths: &[PathBuf],
4334//     app_state: &Arc<AppState>,
4335//     requesting_window: Option<WindowHandle<Workspace>>,
4336//     cx: &mut AppContext,
4337// ) -> Task<
4338//     Result<(
4339//         WeakViewHandle<Workspace>,
4340//         Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
4341//     )>,
4342// > {
4343//     let app_state = app_state.clone();
4344//     let abs_paths = abs_paths.to_vec();
4345//     cx.spawn(|mut cx| async move {
4346//         // Open paths in existing workspace if possible
4347//         let existing = activate_workspace_for_project(&mut cx, |project, cx| {
4348//             project.contains_paths(&abs_paths, cx)
4349//         });
4350
4351//         if let Some(existing) = existing {
4352//             Ok((
4353//                 existing.clone(),
4354//                 existing
4355//                     .update(&mut cx, |workspace, cx| {
4356//                         workspace.open_paths(abs_paths, true, cx)
4357//                     })?
4358//                     .await,
4359//             ))
4360//         } else {
4361//             Ok(cx
4362//                 .update(|cx| {
4363//                     Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
4364//                 })
4365//                 .await)
4366//         }
4367//     })
4368// }
4369
4370// pub fn open_new(
4371//     app_state: &Arc<AppState>,
4372//     cx: &mut AppContext,
4373//     init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static,
4374// ) -> Task<()> {
4375//     let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
4376//     cx.spawn(|mut cx| async move {
4377//         let (workspace, opened_paths) = task.await;
4378
4379//         workspace
4380//             .update(&mut cx, |workspace, cx| {
4381//                 if opened_paths.is_empty() {
4382//                     init(workspace, cx)
4383//                 }
4384//             })
4385//             .log_err();
4386//     })
4387// }
4388
4389// pub fn create_and_open_local_file(
4390//     path: &'static Path,
4391//     cx: &mut ViewContext<Workspace>,
4392//     default_content: impl 'static + Send + FnOnce() -> Rope,
4393// ) -> Task<Result<Box<dyn ItemHandle>>> {
4394//     cx.spawn(|workspace, mut cx| async move {
4395//         let fs = workspace.read_with(&cx, |workspace, _| workspace.app_state().fs.clone())?;
4396//         if !fs.is_file(path).await {
4397//             fs.create_file(path, Default::default()).await?;
4398//             fs.save(path, &default_content(), Default::default())
4399//                 .await?;
4400//         }
4401
4402//         let mut items = workspace
4403//             .update(&mut cx, |workspace, cx| {
4404//                 workspace.with_local_workspace(cx, |workspace, cx| {
4405//                     workspace.open_paths(vec![path.to_path_buf()], false, cx)
4406//                 })
4407//             })?
4408//             .await?
4409//             .await;
4410
4411//         let item = items.pop().flatten();
4412//         item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
4413//     })
4414// }
4415
4416// pub fn join_remote_project(
4417//     project_id: u64,
4418//     follow_user_id: u64,
4419//     app_state: Arc<AppState>,
4420//     cx: &mut AppContext,
4421// ) -> Task<Result<()>> {
4422//     cx.spawn(|mut cx| async move {
4423//         let windows = cx.windows();
4424//         let existing_workspace = windows.into_iter().find_map(|window| {
4425//             window.downcast::<Workspace>().and_then(|window| {
4426//                 window
4427//                     .read_root_with(&cx, |workspace, cx| {
4428//                         if workspace.project().read(cx).remote_id() == Some(project_id) {
4429//                             Some(cx.handle().downgrade())
4430//                         } else {
4431//                             None
4432//                         }
4433//                     })
4434//                     .unwrap_or(None)
4435//             })
4436//         });
4437
4438//         let workspace = if let Some(existing_workspace) = existing_workspace {
4439//             existing_workspace
4440//         } else {
4441//             let active_call = cx.read(ActiveCall::global);
4442//             let room = active_call
4443//                 .read_with(&cx, |call, _| call.room().cloned())
4444//                 .ok_or_else(|| anyhow!("not in a call"))?;
4445//             let project = room
4446//                 .update(&mut cx, |room, cx| {
4447//                     room.join_project(
4448//                         project_id,
4449//                         app_state.languages.clone(),
4450//                         app_state.fs.clone(),
4451//                         cx,
4452//                     )
4453//                 })
4454//                 .await?;
4455
4456//             let window_bounds_override = window_bounds_env_override(&cx);
4457//             let window = cx.add_window(
4458//                 (app_state.build_window_options)(
4459//                     window_bounds_override,
4460//                     None,
4461//                     cx.platform().as_ref(),
4462//                 ),
4463//                 |cx| Workspace::new(0, project, app_state.clone(), cx),
4464//             );
4465//             let workspace = window.root(&cx).unwrap();
4466//             (app_state.initialize_workspace)(
4467//                 workspace.downgrade(),
4468//                 false,
4469//                 app_state.clone(),
4470//                 cx.clone(),
4471//             )
4472//             .await
4473//             .log_err();
4474
4475//             workspace.downgrade()
4476//         };
4477
4478//         workspace.window().activate(&mut cx);
4479//         cx.platform().activate(true);
4480
4481//         workspace.update(&mut cx, |workspace, cx| {
4482//             if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
4483//                 let follow_peer_id = room
4484//                     .read(cx)
4485//                     .remote_participants()
4486//                     .iter()
4487//                     .find(|(_, participant)| participant.user.id == follow_user_id)
4488//                     .map(|(_, p)| p.peer_id)
4489//                     .or_else(|| {
4490//                         // If we couldn't follow the given user, follow the host instead.
4491//                         let collaborator = workspace
4492//                             .project()
4493//                             .read(cx)
4494//                             .collaborators()
4495//                             .values()
4496//                             .find(|collaborator| collaborator.replica_id == 0)?;
4497//                         Some(collaborator.peer_id)
4498//                     });
4499
4500//                 if let Some(follow_peer_id) = follow_peer_id {
4501//                     workspace
4502//                         .follow(follow_peer_id, cx)
4503//                         .map(|follow| follow.detach_and_log_err(cx));
4504//                 }
4505//             }
4506//         })?;
4507
4508//         anyhow::Ok(())
4509//     })
4510// }
4511
4512// pub fn restart(_: &Restart, cx: &mut AppContext) {
4513//     let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
4514//     cx.spawn(|mut cx| async move {
4515//         let mut workspace_windows = cx
4516//             .windows()
4517//             .into_iter()
4518//             .filter_map(|window| window.downcast::<Workspace>())
4519//             .collect::<Vec<_>>();
4520
4521//         // If multiple windows have unsaved changes, and need a save prompt,
4522//         // prompt in the active window before switching to a different window.
4523//         workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
4524
4525//         if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
4526//             let answer = window.prompt(
4527//                 PromptLevel::Info,
4528//                 "Are you sure you want to restart?",
4529//                 &["Restart", "Cancel"],
4530//                 &mut cx,
4531//             );
4532
4533//             if let Some(mut answer) = answer {
4534//                 let answer = answer.next().await;
4535//                 if answer != Some(0) {
4536//                     return Ok(());
4537//                 }
4538//             }
4539//         }
4540
4541//         // If the user cancels any save prompt, then keep the app open.
4542//         for window in workspace_windows {
4543//             if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
4544//                 workspace.prepare_to_close(true, cx)
4545//             }) {
4546//                 if !should_close.await? {
4547//                     return Ok(());
4548//                 }
4549//             }
4550//         }
4551//         cx.platform().restart();
4552//         anyhow::Ok(())
4553//     })
4554//     .detach_and_log_err(cx);
4555// }
4556
4557// fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
4558//     let mut parts = value.split(',');
4559//     let width: usize = parts.next()?.parse().ok()?;
4560//     let height: usize = parts.next()?.parse().ok()?;
4561//     Some(vec2f(width as f32, height as f32))
4562// }
4563
4564// #[cfg(test)]
4565// mod tests {
4566//     use super::*;
4567//     use crate::{
4568//         dock::test::{TestPanel, TestPanelEvent},
4569//         item::test::{TestItem, TestItemEvent, TestProjectItem},
4570//     };
4571//     use fs::FakeFs;
4572//     use gpui::{executor::Deterministic, test::EmptyView, TestAppContext};
4573//     use project::{Project, ProjectEntryId};
4574//     use serde_json::json;
4575//     use settings::SettingsStore;
4576//     use std::{cell::RefCell, rc::Rc};
4577
4578//     #[gpui::test]
4579//     async fn test_tab_disambiguation(cx: &mut TestAppContext) {
4580//         init_test(cx);
4581
4582//         let fs = FakeFs::new(cx.background());
4583//         let project = Project::test(fs, [], cx).await;
4584//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4585//         let workspace = window.root(cx);
4586
4587//         // Adding an item with no ambiguity renders the tab without detail.
4588//         let item1 = window.add_view(cx, |_| {
4589//             let mut item = TestItem::new();
4590//             item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
4591//             item
4592//         });
4593//         workspace.update(cx, |workspace, cx| {
4594//             workspace.add_item(Box::new(item1.clone()), cx);
4595//         });
4596//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None));
4597
4598//         // Adding an item that creates ambiguity increases the level of detail on
4599//         // both tabs.
4600//         let item2 = window.add_view(cx, |_| {
4601//             let mut item = TestItem::new();
4602//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4603//             item
4604//         });
4605//         workspace.update(cx, |workspace, cx| {
4606//             workspace.add_item(Box::new(item2.clone()), cx);
4607//         });
4608//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4609//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4610
4611//         // Adding an item that creates ambiguity increases the level of detail only
4612//         // on the ambiguous tabs. In this case, the ambiguity can't be resolved so
4613//         // we stop at the highest detail available.
4614//         let item3 = window.add_view(cx, |_| {
4615//             let mut item = TestItem::new();
4616//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4617//             item
4618//         });
4619//         workspace.update(cx, |workspace, cx| {
4620//             workspace.add_item(Box::new(item3.clone()), cx);
4621//         });
4622//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4623//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4624//         item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4625//     }
4626
4627//     #[gpui::test]
4628//     async fn test_tracking_active_path(cx: &mut TestAppContext) {
4629//         init_test(cx);
4630
4631//         let fs = FakeFs::new(cx.background());
4632//         fs.insert_tree(
4633//             "/root1",
4634//             json!({
4635//                 "one.txt": "",
4636//                 "two.txt": "",
4637//             }),
4638//         )
4639//         .await;
4640//         fs.insert_tree(
4641//             "/root2",
4642//             json!({
4643//                 "three.txt": "",
4644//             }),
4645//         )
4646//         .await;
4647
4648//         let project = Project::test(fs, ["root1".as_ref()], cx).await;
4649//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4650//         let workspace = window.root(cx);
4651//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4652//         let worktree_id = project.read_with(cx, |project, cx| {
4653//             project.worktrees(cx).next().unwrap().read(cx).id()
4654//         });
4655
4656//         let item1 = window.add_view(cx, |cx| {
4657//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
4658//         });
4659//         let item2 = window.add_view(cx, |cx| {
4660//             TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
4661//         });
4662
4663//         // Add an item to an empty pane
4664//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx));
4665//         project.read_with(cx, |project, cx| {
4666//             assert_eq!(
4667//                 project.active_entry(),
4668//                 project
4669//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4670//                     .map(|e| e.id)
4671//             );
4672//         });
4673//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4674
4675//         // Add a second item to a non-empty pane
4676//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
4677//         assert_eq!(window.current_title(cx).as_deref(), Some("two.txt β€” root1"));
4678//         project.read_with(cx, |project, cx| {
4679//             assert_eq!(
4680//                 project.active_entry(),
4681//                 project
4682//                     .entry_for_path(&(worktree_id, "two.txt").into(), cx)
4683//                     .map(|e| e.id)
4684//             );
4685//         });
4686
4687//         // Close the active item
4688//         pane.update(cx, |pane, cx| {
4689//             pane.close_active_item(&Default::default(), cx).unwrap()
4690//         })
4691//         .await
4692//         .unwrap();
4693//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4694//         project.read_with(cx, |project, cx| {
4695//             assert_eq!(
4696//                 project.active_entry(),
4697//                 project
4698//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4699//                     .map(|e| e.id)
4700//             );
4701//         });
4702
4703//         // Add a project folder
4704//         project
4705//             .update(cx, |project, cx| {
4706//                 project.find_or_create_local_worktree("/root2", true, cx)
4707//             })
4708//             .await
4709//             .unwrap();
4710//         assert_eq!(
4711//             window.current_title(cx).as_deref(),
4712//             Some("one.txt β€” root1, root2")
4713//         );
4714
4715//         // Remove a project folder
4716//         project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
4717//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root2"));
4718//     }
4719
4720//     #[gpui::test]
4721//     async fn test_close_window(cx: &mut TestAppContext) {
4722//         init_test(cx);
4723
4724//         let fs = FakeFs::new(cx.background());
4725//         fs.insert_tree("/root", json!({ "one": "" })).await;
4726
4727//         let project = Project::test(fs, ["root".as_ref()], cx).await;
4728//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4729//         let workspace = window.root(cx);
4730
4731//         // When there are no dirty items, there's nothing to do.
4732//         let item1 = window.add_view(cx, |_| TestItem::new());
4733//         workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
4734//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4735//         assert!(task.await.unwrap());
4736
4737//         // When there are dirty untitled items, prompt to save each one. If the user
4738//         // cancels any prompt, then abort.
4739//         let item2 = window.add_view(cx, |_| TestItem::new().with_dirty(true));
4740//         let item3 = window.add_view(cx, |cx| {
4741//             TestItem::new()
4742//                 .with_dirty(true)
4743//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4744//         });
4745//         workspace.update(cx, |w, cx| {
4746//             w.add_item(Box::new(item2.clone()), cx);
4747//             w.add_item(Box::new(item3.clone()), cx);
4748//         });
4749//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4750//         cx.foreground().run_until_parked();
4751//         window.simulate_prompt_answer(2, cx); // cancel save all
4752//         cx.foreground().run_until_parked();
4753//         window.simulate_prompt_answer(2, cx); // cancel save all
4754//         cx.foreground().run_until_parked();
4755//         assert!(!window.has_pending_prompt(cx));
4756//         assert!(!task.await.unwrap());
4757//     }
4758
4759//     #[gpui::test]
4760//     async fn test_close_pane_items(cx: &mut TestAppContext) {
4761//         init_test(cx);
4762
4763//         let fs = FakeFs::new(cx.background());
4764
4765//         let project = Project::test(fs, None, cx).await;
4766//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4767//         let workspace = window.root(cx);
4768
4769//         let item1 = window.add_view(cx, |cx| {
4770//             TestItem::new()
4771//                 .with_dirty(true)
4772//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4773//         });
4774//         let item2 = window.add_view(cx, |cx| {
4775//             TestItem::new()
4776//                 .with_dirty(true)
4777//                 .with_conflict(true)
4778//                 .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
4779//         });
4780//         let item3 = window.add_view(cx, |cx| {
4781//             TestItem::new()
4782//                 .with_dirty(true)
4783//                 .with_conflict(true)
4784//                 .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
4785//         });
4786//         let item4 = window.add_view(cx, |cx| {
4787//             TestItem::new()
4788//                 .with_dirty(true)
4789//                 .with_project_items(&[TestProjectItem::new_untitled(cx)])
4790//         });
4791//         let pane = workspace.update(cx, |workspace, cx| {
4792//             workspace.add_item(Box::new(item1.clone()), cx);
4793//             workspace.add_item(Box::new(item2.clone()), cx);
4794//             workspace.add_item(Box::new(item3.clone()), cx);
4795//             workspace.add_item(Box::new(item4.clone()), cx);
4796//             workspace.active_pane().clone()
4797//         });
4798
4799//         let close_items = pane.update(cx, |pane, cx| {
4800//             pane.activate_item(1, true, true, cx);
4801//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4802//             let item1_id = item1.id();
4803//             let item3_id = item3.id();
4804//             let item4_id = item4.id();
4805//             pane.close_items(cx, SaveIntent::Close, move |id| {
4806//                 [item1_id, item3_id, item4_id].contains(&id)
4807//             })
4808//         });
4809//         cx.foreground().run_until_parked();
4810
4811//         assert!(window.has_pending_prompt(cx));
4812//         // Ignore "Save all" prompt
4813//         window.simulate_prompt_answer(2, cx);
4814//         cx.foreground().run_until_parked();
4815//         // There's a prompt to save item 1.
4816//         pane.read_with(cx, |pane, _| {
4817//             assert_eq!(pane.items_len(), 4);
4818//             assert_eq!(pane.active_item().unwrap().id(), item1.id());
4819//         });
4820//         // Confirm saving item 1.
4821//         window.simulate_prompt_answer(0, cx);
4822//         cx.foreground().run_until_parked();
4823
4824//         // Item 1 is saved. There's a prompt to save item 3.
4825//         pane.read_with(cx, |pane, cx| {
4826//             assert_eq!(item1.read(cx).save_count, 1);
4827//             assert_eq!(item1.read(cx).save_as_count, 0);
4828//             assert_eq!(item1.read(cx).reload_count, 0);
4829//             assert_eq!(pane.items_len(), 3);
4830//             assert_eq!(pane.active_item().unwrap().id(), item3.id());
4831//         });
4832//         assert!(window.has_pending_prompt(cx));
4833
4834//         // Cancel saving item 3.
4835//         window.simulate_prompt_answer(1, cx);
4836//         cx.foreground().run_until_parked();
4837
4838//         // Item 3 is reloaded. There's a prompt to save item 4.
4839//         pane.read_with(cx, |pane, cx| {
4840//             assert_eq!(item3.read(cx).save_count, 0);
4841//             assert_eq!(item3.read(cx).save_as_count, 0);
4842//             assert_eq!(item3.read(cx).reload_count, 1);
4843//             assert_eq!(pane.items_len(), 2);
4844//             assert_eq!(pane.active_item().unwrap().id(), item4.id());
4845//         });
4846//         assert!(window.has_pending_prompt(cx));
4847
4848//         // Confirm saving item 4.
4849//         window.simulate_prompt_answer(0, cx);
4850//         cx.foreground().run_until_parked();
4851
4852//         // There's a prompt for a path for item 4.
4853//         cx.simulate_new_path_selection(|_| Some(Default::default()));
4854//         close_items.await.unwrap();
4855
4856//         // The requested items are closed.
4857//         pane.read_with(cx, |pane, cx| {
4858//             assert_eq!(item4.read(cx).save_count, 0);
4859//             assert_eq!(item4.read(cx).save_as_count, 1);
4860//             assert_eq!(item4.read(cx).reload_count, 0);
4861//             assert_eq!(pane.items_len(), 1);
4862//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4863//         });
4864//     }
4865
4866//     #[gpui::test]
4867//     async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) {
4868//         init_test(cx);
4869
4870//         let fs = FakeFs::new(cx.background());
4871
4872//         let project = Project::test(fs, [], cx).await;
4873//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4874//         let workspace = window.root(cx);
4875
4876//         // Create several workspace items with single project entries, and two
4877//         // workspace items with multiple project entries.
4878//         let single_entry_items = (0..=4)
4879//             .map(|project_entry_id| {
4880//                 window.add_view(cx, |cx| {
4881//                     TestItem::new()
4882//                         .with_dirty(true)
4883//                         .with_project_items(&[TestProjectItem::new(
4884//                             project_entry_id,
4885//                             &format!("{project_entry_id}.txt"),
4886//                             cx,
4887//                         )])
4888//                 })
4889//             })
4890//             .collect::<Vec<_>>();
4891//         let item_2_3 = window.add_view(cx, |cx| {
4892//             TestItem::new()
4893//                 .with_dirty(true)
4894//                 .with_singleton(false)
4895//                 .with_project_items(&[
4896//                     single_entry_items[2].read(cx).project_items[0].clone(),
4897//                     single_entry_items[3].read(cx).project_items[0].clone(),
4898//                 ])
4899//         });
4900//         let item_3_4 = window.add_view(cx, |cx| {
4901//             TestItem::new()
4902//                 .with_dirty(true)
4903//                 .with_singleton(false)
4904//                 .with_project_items(&[
4905//                     single_entry_items[3].read(cx).project_items[0].clone(),
4906//                     single_entry_items[4].read(cx).project_items[0].clone(),
4907//                 ])
4908//         });
4909
4910//         // Create two panes that contain the following project entries:
4911//         //   left pane:
4912//         //     multi-entry items:   (2, 3)
4913//         //     single-entry items:  0, 1, 2, 3, 4
4914//         //   right pane:
4915//         //     single-entry items:  1
4916//         //     multi-entry items:   (3, 4)
4917//         let left_pane = workspace.update(cx, |workspace, cx| {
4918//             let left_pane = workspace.active_pane().clone();
4919//             workspace.add_item(Box::new(item_2_3.clone()), cx);
4920//             for item in single_entry_items {
4921//                 workspace.add_item(Box::new(item), cx);
4922//             }
4923//             left_pane.update(cx, |pane, cx| {
4924//                 pane.activate_item(2, true, true, cx);
4925//             });
4926
4927//             workspace
4928//                 .split_and_clone(left_pane.clone(), SplitDirection::Right, cx)
4929//                 .unwrap();
4930
4931//             left_pane
4932//         });
4933
4934//         //Need to cause an effect flush in order to respect new focus
4935//         workspace.update(cx, |workspace, cx| {
4936//             workspace.add_item(Box::new(item_3_4.clone()), cx);
4937//             cx.focus(&left_pane);
4938//         });
4939
4940//         // When closing all of the items in the left pane, we should be prompted twice:
4941//         // once for project entry 0, and once for project entry 2. After those two
4942//         // prompts, the task should complete.
4943
4944//         let close = left_pane.update(cx, |pane, cx| {
4945//             pane.close_items(cx, SaveIntent::Close, move |_| true)
4946//         });
4947//         cx.foreground().run_until_parked();
4948//         // Discard "Save all" prompt
4949//         window.simulate_prompt_answer(2, cx);
4950
4951//         cx.foreground().run_until_parked();
4952//         left_pane.read_with(cx, |pane, cx| {
4953//             assert_eq!(
4954//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
4955//                 &[ProjectEntryId::from_proto(0)]
4956//             );
4957//         });
4958//         window.simulate_prompt_answer(0, cx);
4959
4960//         cx.foreground().run_until_parked();
4961//         left_pane.read_with(cx, |pane, cx| {
4962//             assert_eq!(
4963//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
4964//                 &[ProjectEntryId::from_proto(2)]
4965//             );
4966//         });
4967//         window.simulate_prompt_answer(0, cx);
4968
4969//         cx.foreground().run_until_parked();
4970//         close.await.unwrap();
4971//         left_pane.read_with(cx, |pane, _| {
4972//             assert_eq!(pane.items_len(), 0);
4973//         });
4974//     }
4975
4976//     #[gpui::test]
4977//     async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
4978//         init_test(cx);
4979
4980//         let fs = FakeFs::new(cx.background());
4981
4982//         let project = Project::test(fs, [], cx).await;
4983//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4984//         let workspace = window.root(cx);
4985//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4986
4987//         let item = window.add_view(cx, |cx| {
4988//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4989//         });
4990//         let item_id = item.id();
4991//         workspace.update(cx, |workspace, cx| {
4992//             workspace.add_item(Box::new(item.clone()), cx);
4993//         });
4994
4995//         // Autosave on window change.
4996//         item.update(cx, |item, cx| {
4997//             cx.update_global(|settings: &mut SettingsStore, cx| {
4998//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
4999//                     settings.autosave = Some(AutosaveSetting::OnWindowChange);
5000//                 })
5001//             });
5002//             item.is_dirty = true;
5003//         });
5004
5005//         // Deactivating the window saves the file.
5006//         window.simulate_deactivation(cx);
5007//         deterministic.run_until_parked();
5008//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
5009
5010//         // Autosave on focus change.
5011//         item.update(cx, |item, cx| {
5012//             cx.focus_self();
5013//             cx.update_global(|settings: &mut SettingsStore, cx| {
5014//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5015//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5016//                 })
5017//             });
5018//             item.is_dirty = true;
5019//         });
5020
5021//         // Blurring the item saves the file.
5022//         item.update(cx, |_, cx| cx.blur());
5023//         deterministic.run_until_parked();
5024//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
5025
5026//         // Deactivating the window still saves the file.
5027//         window.simulate_activation(cx);
5028//         item.update(cx, |item, cx| {
5029//             cx.focus_self();
5030//             item.is_dirty = true;
5031//         });
5032//         window.simulate_deactivation(cx);
5033
5034//         deterministic.run_until_parked();
5035//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5036
5037//         // Autosave after delay.
5038//         item.update(cx, |item, cx| {
5039//             cx.update_global(|settings: &mut SettingsStore, cx| {
5040//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5041//                     settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
5042//                 })
5043//             });
5044//             item.is_dirty = true;
5045//             cx.emit(TestItemEvent::Edit);
5046//         });
5047
5048//         // Delay hasn't fully expired, so the file is still dirty and unsaved.
5049//         deterministic.advance_clock(Duration::from_millis(250));
5050//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5051
5052//         // After delay expires, the file is saved.
5053//         deterministic.advance_clock(Duration::from_millis(250));
5054//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 4));
5055
5056//         // Autosave on focus change, ensuring closing the tab counts as such.
5057//         item.update(cx, |item, cx| {
5058//             cx.update_global(|settings: &mut SettingsStore, cx| {
5059//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5060//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5061//                 })
5062//             });
5063//             item.is_dirty = true;
5064//         });
5065
5066//         pane.update(cx, |pane, cx| {
5067//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5068//         })
5069//         .await
5070//         .unwrap();
5071//         assert!(!window.has_pending_prompt(cx));
5072//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5073
5074//         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
5075//         workspace.update(cx, |workspace, cx| {
5076//             workspace.add_item(Box::new(item.clone()), cx);
5077//         });
5078//         item.update(cx, |item, cx| {
5079//             item.project_items[0].update(cx, |item, _| {
5080//                 item.entry_id = None;
5081//             });
5082//             item.is_dirty = true;
5083//             cx.blur();
5084//         });
5085//         deterministic.run_until_parked();
5086//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5087
5088//         // Ensure autosave is prevented for deleted files also when closing the buffer.
5089//         let _close_items = pane.update(cx, |pane, cx| {
5090//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5091//         });
5092//         deterministic.run_until_parked();
5093//         assert!(window.has_pending_prompt(cx));
5094//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5095//     }
5096
5097//     #[gpui::test]
5098//     async fn test_pane_navigation(cx: &mut gpui::TestAppContext) {
5099//         init_test(cx);
5100
5101//         let fs = FakeFs::new(cx.background());
5102
5103//         let project = Project::test(fs, [], cx).await;
5104//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5105//         let workspace = window.root(cx);
5106
5107//         let item = window.add_view(cx, |cx| {
5108//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
5109//         });
5110//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5111//         let toolbar = pane.read_with(cx, |pane, _| pane.toolbar().clone());
5112//         let toolbar_notify_count = Rc::new(RefCell::new(0));
5113
5114//         workspace.update(cx, |workspace, cx| {
5115//             workspace.add_item(Box::new(item.clone()), cx);
5116//             let toolbar_notification_count = toolbar_notify_count.clone();
5117//             cx.observe(&toolbar, move |_, _, _| {
5118//                 *toolbar_notification_count.borrow_mut() += 1
5119//             })
5120//             .detach();
5121//         });
5122
5123//         pane.read_with(cx, |pane, _| {
5124//             assert!(!pane.can_navigate_backward());
5125//             assert!(!pane.can_navigate_forward());
5126//         });
5127
5128//         item.update(cx, |item, cx| {
5129//             item.set_state("one".to_string(), cx);
5130//         });
5131
5132//         // Toolbar must be notified to re-render the navigation buttons
5133//         assert_eq!(*toolbar_notify_count.borrow(), 1);
5134
5135//         pane.read_with(cx, |pane, _| {
5136//             assert!(pane.can_navigate_backward());
5137//             assert!(!pane.can_navigate_forward());
5138//         });
5139
5140//         workspace
5141//             .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
5142//             .await
5143//             .unwrap();
5144
5145//         assert_eq!(*toolbar_notify_count.borrow(), 3);
5146//         pane.read_with(cx, |pane, _| {
5147//             assert!(!pane.can_navigate_backward());
5148//             assert!(pane.can_navigate_forward());
5149//         });
5150//     }
5151
5152//     #[gpui::test]
5153//     async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) {
5154//         init_test(cx);
5155//         let fs = FakeFs::new(cx.background());
5156
5157//         let project = Project::test(fs, [], cx).await;
5158//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5159//         let workspace = window.root(cx);
5160
5161//         let panel = workspace.update(cx, |workspace, cx| {
5162//             let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right));
5163//             workspace.add_panel(panel.clone(), cx);
5164
5165//             workspace
5166//                 .right_dock()
5167//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5168
5169//             panel
5170//         });
5171
5172//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5173//         pane.update(cx, |pane, cx| {
5174//             let item = cx.add_view(|_| TestItem::new());
5175//             pane.add_item(Box::new(item), true, true, None, cx);
5176//         });
5177
5178//         // Transfer focus from center to panel
5179//         workspace.update(cx, |workspace, cx| {
5180//             workspace.toggle_panel_focus::<TestPanel>(cx);
5181//         });
5182
5183//         workspace.read_with(cx, |workspace, cx| {
5184//             assert!(workspace.right_dock().read(cx).is_open());
5185//             assert!(!panel.is_zoomed(cx));
5186//             assert!(panel.has_focus(cx));
5187//         });
5188
5189//         // Transfer focus from panel to center
5190//         workspace.update(cx, |workspace, cx| {
5191//             workspace.toggle_panel_focus::<TestPanel>(cx);
5192//         });
5193
5194//         workspace.read_with(cx, |workspace, cx| {
5195//             assert!(workspace.right_dock().read(cx).is_open());
5196//             assert!(!panel.is_zoomed(cx));
5197//             assert!(!panel.has_focus(cx));
5198//         });
5199
5200//         // Close the dock
5201//         workspace.update(cx, |workspace, cx| {
5202//             workspace.toggle_dock(DockPosition::Right, cx);
5203//         });
5204
5205//         workspace.read_with(cx, |workspace, cx| {
5206//             assert!(!workspace.right_dock().read(cx).is_open());
5207//             assert!(!panel.is_zoomed(cx));
5208//             assert!(!panel.has_focus(cx));
5209//         });
5210
5211//         // Open the dock
5212//         workspace.update(cx, |workspace, cx| {
5213//             workspace.toggle_dock(DockPosition::Right, cx);
5214//         });
5215
5216//         workspace.read_with(cx, |workspace, cx| {
5217//             assert!(workspace.right_dock().read(cx).is_open());
5218//             assert!(!panel.is_zoomed(cx));
5219//             assert!(panel.has_focus(cx));
5220//         });
5221
5222//         // Focus and zoom panel
5223//         panel.update(cx, |panel, cx| {
5224//             cx.focus_self();
5225//             panel.set_zoomed(true, cx)
5226//         });
5227
5228//         workspace.read_with(cx, |workspace, cx| {
5229//             assert!(workspace.right_dock().read(cx).is_open());
5230//             assert!(panel.is_zoomed(cx));
5231//             assert!(panel.has_focus(cx));
5232//         });
5233
5234//         // Transfer focus to the center closes the dock
5235//         workspace.update(cx, |workspace, cx| {
5236//             workspace.toggle_panel_focus::<TestPanel>(cx);
5237//         });
5238
5239//         workspace.read_with(cx, |workspace, cx| {
5240//             assert!(!workspace.right_dock().read(cx).is_open());
5241//             assert!(panel.is_zoomed(cx));
5242//             assert!(!panel.has_focus(cx));
5243//         });
5244
5245//         // Transferring focus back to the panel keeps it zoomed
5246//         workspace.update(cx, |workspace, cx| {
5247//             workspace.toggle_panel_focus::<TestPanel>(cx);
5248//         });
5249
5250//         workspace.read_with(cx, |workspace, cx| {
5251//             assert!(workspace.right_dock().read(cx).is_open());
5252//             assert!(panel.is_zoomed(cx));
5253//             assert!(panel.has_focus(cx));
5254//         });
5255
5256//         // Close the dock while it is zoomed
5257//         workspace.update(cx, |workspace, cx| {
5258//             workspace.toggle_dock(DockPosition::Right, cx)
5259//         });
5260
5261//         workspace.read_with(cx, |workspace, cx| {
5262//             assert!(!workspace.right_dock().read(cx).is_open());
5263//             assert!(panel.is_zoomed(cx));
5264//             assert!(workspace.zoomed.is_none());
5265//             assert!(!panel.has_focus(cx));
5266//         });
5267
5268//         // Opening the dock, when it's zoomed, retains focus
5269//         workspace.update(cx, |workspace, cx| {
5270//             workspace.toggle_dock(DockPosition::Right, cx)
5271//         });
5272
5273//         workspace.read_with(cx, |workspace, cx| {
5274//             assert!(workspace.right_dock().read(cx).is_open());
5275//             assert!(panel.is_zoomed(cx));
5276//             assert!(workspace.zoomed.is_some());
5277//             assert!(panel.has_focus(cx));
5278//         });
5279
5280//         // Unzoom and close the panel, zoom the active pane.
5281//         panel.update(cx, |panel, cx| panel.set_zoomed(false, cx));
5282//         workspace.update(cx, |workspace, cx| {
5283//             workspace.toggle_dock(DockPosition::Right, cx)
5284//         });
5285//         pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx));
5286
5287//         // Opening a dock unzooms the pane.
5288//         workspace.update(cx, |workspace, cx| {
5289//             workspace.toggle_dock(DockPosition::Right, cx)
5290//         });
5291//         workspace.read_with(cx, |workspace, cx| {
5292//             let pane = pane.read(cx);
5293//             assert!(!pane.is_zoomed());
5294//             assert!(!pane.has_focus());
5295//             assert!(workspace.right_dock().read(cx).is_open());
5296//             assert!(workspace.zoomed.is_none());
5297//         });
5298//     }
5299
5300//     #[gpui::test]
5301//     async fn test_panels(cx: &mut gpui::TestAppContext) {
5302//         init_test(cx);
5303//         let fs = FakeFs::new(cx.background());
5304
5305//         let project = Project::test(fs, [], cx).await;
5306//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5307//         let workspace = window.root(cx);
5308
5309//         let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| {
5310//             // Add panel_1 on the left, panel_2 on the right.
5311//             let panel_1 = cx.add_view(|_| TestPanel::new(DockPosition::Left));
5312//             workspace.add_panel(panel_1.clone(), cx);
5313//             workspace
5314//                 .left_dock()
5315//                 .update(cx, |left_dock, cx| left_dock.set_open(true, cx));
5316//             let panel_2 = cx.add_view(|_| TestPanel::new(DockPosition::Right));
5317//             workspace.add_panel(panel_2.clone(), cx);
5318//             workspace
5319//                 .right_dock()
5320//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5321
5322//             let left_dock = workspace.left_dock();
5323//             assert_eq!(
5324//                 left_dock.read(cx).visible_panel().unwrap().id(),
5325//                 panel_1.id()
5326//             );
5327//             assert_eq!(
5328//                 left_dock.read(cx).active_panel_size(cx).unwrap(),
5329//                 panel_1.size(cx)
5330//             );
5331
5332//             left_dock.update(cx, |left_dock, cx| {
5333//                 left_dock.resize_active_panel(Some(1337.), cx)
5334//             });
5335//             assert_eq!(
5336//                 workspace
5337//                     .right_dock()
5338//                     .read(cx)
5339//                     .visible_panel()
5340//                     .unwrap()
5341//                     .id(),
5342//                 panel_2.id()
5343//             );
5344
5345//             (panel_1, panel_2)
5346//         });
5347
5348//         // Move panel_1 to the right
5349//         panel_1.update(cx, |panel_1, cx| {
5350//             panel_1.set_position(DockPosition::Right, cx)
5351//         });
5352
5353//         workspace.update(cx, |workspace, cx| {
5354//             // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right.
5355//             // Since it was the only panel on the left, the left dock should now be closed.
5356//             assert!(!workspace.left_dock().read(cx).is_open());
5357//             assert!(workspace.left_dock().read(cx).visible_panel().is_none());
5358//             let right_dock = workspace.right_dock();
5359//             assert_eq!(
5360//                 right_dock.read(cx).visible_panel().unwrap().id(),
5361//                 panel_1.id()
5362//             );
5363//             assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5364
5365//             // Now we move panel_2Β to the left
5366//             panel_2.set_position(DockPosition::Left, cx);
5367//         });
5368
5369//         workspace.update(cx, |workspace, cx| {
5370//             // Since panel_2 was not visible on the right, we don't open the left dock.
5371//             assert!(!workspace.left_dock().read(cx).is_open());
5372//             // And the right dock is unaffected in it's displaying of panel_1
5373//             assert!(workspace.right_dock().read(cx).is_open());
5374//             assert_eq!(
5375//                 workspace
5376//                     .right_dock()
5377//                     .read(cx)
5378//                     .visible_panel()
5379//                     .unwrap()
5380//                     .id(),
5381//                 panel_1.id()
5382//             );
5383//         });
5384
5385//         // Move panel_1 back to the left
5386//         panel_1.update(cx, |panel_1, cx| {
5387//             panel_1.set_position(DockPosition::Left, cx)
5388//         });
5389
5390//         workspace.update(cx, |workspace, cx| {
5391//             // Since panel_1 was visible on the right, we open the left dock and make panel_1 active.
5392//             let left_dock = workspace.left_dock();
5393//             assert!(left_dock.read(cx).is_open());
5394//             assert_eq!(
5395//                 left_dock.read(cx).visible_panel().unwrap().id(),
5396//                 panel_1.id()
5397//             );
5398//             assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5399//             // And right the dock should be closed as it no longer has any panels.
5400//             assert!(!workspace.right_dock().read(cx).is_open());
5401
5402//             // Now we move panel_1 to the bottom
5403//             panel_1.set_position(DockPosition::Bottom, cx);
5404//         });
5405
5406//         workspace.update(cx, |workspace, cx| {
5407//             // Since panel_1 was visible on the left, we close the left dock.
5408//             assert!(!workspace.left_dock().read(cx).is_open());
5409//             // The bottom dock is sized based on the panel's default size,
5410//             // since the panel orientation changed from vertical to horizontal.
5411//             let bottom_dock = workspace.bottom_dock();
5412//             assert_eq!(
5413//                 bottom_dock.read(cx).active_panel_size(cx).unwrap(),
5414//                 panel_1.size(cx),
5415//             );
5416//             // Close bottom dock and move panel_1 back to the left.
5417//             bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx));
5418//             panel_1.set_position(DockPosition::Left, cx);
5419//         });
5420
5421//         // Emit activated event on panel 1
5422//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Activated));
5423
5424//         // Now the left dock is open and panel_1 is active and focused.
5425//         workspace.read_with(cx, |workspace, cx| {
5426//             let left_dock = workspace.left_dock();
5427//             assert!(left_dock.read(cx).is_open());
5428//             assert_eq!(
5429//                 left_dock.read(cx).visible_panel().unwrap().id(),
5430//                 panel_1.id()
5431//             );
5432//             assert!(panel_1.is_focused(cx));
5433//         });
5434
5435//         // Emit closed event on panel 2, which is not active
5436//         panel_2.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5437
5438//         // Wo don't close the left dock, because panel_2 wasn't the active panel
5439//         workspace.read_with(cx, |workspace, cx| {
5440//             let left_dock = workspace.left_dock();
5441//             assert!(left_dock.read(cx).is_open());
5442//             assert_eq!(
5443//                 left_dock.read(cx).visible_panel().unwrap().id(),
5444//                 panel_1.id()
5445//             );
5446//         });
5447
5448//         // Emitting a ZoomIn event shows the panel as zoomed.
5449//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn));
5450//         workspace.read_with(cx, |workspace, _| {
5451//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5452//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Left));
5453//         });
5454
5455//         // Move panel to another dock while it is zoomed
5456//         panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx));
5457//         workspace.read_with(cx, |workspace, _| {
5458//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5459//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5460//         });
5461
5462//         // If focus is transferred to another view that's not a panel or another pane, we still show
5463//         // the panel as zoomed.
5464//         let focus_receiver = window.add_view(cx, |_| EmptyView);
5465//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5466//         workspace.read_with(cx, |workspace, _| {
5467//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5468//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5469//         });
5470
5471//         // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed.
5472//         workspace.update(cx, |_, cx| cx.focus_self());
5473//         workspace.read_with(cx, |workspace, _| {
5474//             assert_eq!(workspace.zoomed, None);
5475//             assert_eq!(workspace.zoomed_position, None);
5476//         });
5477
5478//         // If focus is transferred again to another view that's not a panel or a pane, we won't
5479//         // show the panel as zoomed because it wasn't zoomed before.
5480//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5481//         workspace.read_with(cx, |workspace, _| {
5482//             assert_eq!(workspace.zoomed, None);
5483//             assert_eq!(workspace.zoomed_position, None);
5484//         });
5485
5486//         // When focus is transferred back to the panel, it is zoomed again.
5487//         panel_1.update(cx, |_, cx| cx.focus_self());
5488//         workspace.read_with(cx, |workspace, _| {
5489//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5490//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5491//         });
5492
5493//         // Emitting a ZoomOut event unzooms the panel.
5494//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut));
5495//         workspace.read_with(cx, |workspace, _| {
5496//             assert_eq!(workspace.zoomed, None);
5497//             assert_eq!(workspace.zoomed_position, None);
5498//         });
5499
5500//         // Emit closed event on panel 1, which is active
5501//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5502
5503//         // Now the left dock is closed, because panel_1 was the active panel
5504//         workspace.read_with(cx, |workspace, cx| {
5505//             let right_dock = workspace.right_dock();
5506//             assert!(!right_dock.read(cx).is_open());
5507//         });
5508//     }
5509
5510//     pub fn init_test(cx: &mut TestAppContext) {
5511//         cx.foreground().forbid_parking();
5512//         cx.update(|cx| {
5513//             cx.set_global(SettingsStore::test(cx));
5514//             theme::init((), cx);
5515//             language::init(cx);
5516//             crate::init_settings(cx);
5517//             Project::init_settings(cx);
5518//         });
5519//     }
5520// }