workspace2.rs

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