workspace2.rs

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