workspace2.rs

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