workspace2.rs

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