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