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, Div, Entity, EntityId, EventEmitter,
  41    FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render, Size,
  42    StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task,
  43    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, Button, ButtonVariant, Label, LabelColor};
  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, StatelessInteractivity<Workspace>>,
 538            ) -> Div<Workspace, StatelessInteractivity<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        //todo!(saveing)
1328        // let active_call = self.active_call().cloned();
1329        // let window = cx.window();
1330
1331        cx.spawn(|this, mut cx| async move {
1332            // let workspace_count = cx
1333            //     .windows()
1334            //     .into_iter()
1335            //     .filter(|window| window.root_is::<Workspace>())
1336            //     .count();
1337
1338            // if let Some(active_call) = active_call {
1339            //     if !quitting
1340            //         && workspace_count == 1
1341            //         && active_call.read_with(&cx, |call, _| call.room().is_some())
1342            //     {
1343            //         let answer = window.prompt(
1344            //             PromptLevel::Warning,
1345            //             "Do you want to leave the current call?",
1346            //             &["Close window and hang up", "Cancel"],
1347            //             &mut cx,
1348            //         );
1349
1350            //         if let Some(mut answer) = answer {
1351            //             if answer.next().await == Some(1) {
1352            //                 return anyhow::Ok(false);
1353            //             } else {
1354            //                 active_call
1355            //                     .update(&mut cx, |call, cx| call.hang_up(cx))
1356            //                     .await
1357            //                     .log_err();
1358            //             }
1359            //         }
1360            //     }
1361            // }
1362
1363            Ok(
1364                false, // this
1365                      // .update(&mut cx, |this, cx| {
1366                      //     this.save_all_internal(SaveIntent::Close, cx)
1367                      // })?
1368                      // .await?
1369            )
1370        })
1371    }
1372
1373    //     fn save_all(
1374    //         &mut self,
1375    //         action: &SaveAll,
1376    //         cx: &mut ViewContext<Self>,
1377    //     ) -> Option<Task<Result<()>>> {
1378    //         let save_all =
1379    //             self.save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx);
1380    //         Some(cx.foreground().spawn(async move {
1381    //             save_all.await?;
1382    //             Ok(())
1383    //         }))
1384    //     }
1385
1386    //     fn save_all_internal(
1387    //         &mut self,
1388    //         mut save_intent: SaveIntent,
1389    //         cx: &mut ViewContext<Self>,
1390    //     ) -> Task<Result<bool>> {
1391    //         if self.project.read(cx).is_read_only() {
1392    //             return Task::ready(Ok(true));
1393    //         }
1394    //         let dirty_items = self
1395    //             .panes
1396    //             .iter()
1397    //             .flat_map(|pane| {
1398    //                 pane.read(cx).items().filter_map(|item| {
1399    //                     if item.is_dirty(cx) {
1400    //                         Some((pane.downgrade(), item.boxed_clone()))
1401    //                     } else {
1402    //                         None
1403    //                     }
1404    //                 })
1405    //             })
1406    //             .collect::<Vec<_>>();
1407
1408    //         let project = self.project.clone();
1409    //         cx.spawn(|workspace, mut cx| async move {
1410    //             // Override save mode and display "Save all files" prompt
1411    //             if save_intent == SaveIntent::Close && dirty_items.len() > 1 {
1412    //                 let mut answer = workspace.update(&mut cx, |_, cx| {
1413    //                     let prompt = Pane::file_names_for_prompt(
1414    //                         &mut dirty_items.iter().map(|(_, handle)| handle),
1415    //                         dirty_items.len(),
1416    //                         cx,
1417    //                     );
1418    //                     cx.prompt(
1419    //                         PromptLevel::Warning,
1420    //                         &prompt,
1421    //                         &["Save all", "Discard all", "Cancel"],
1422    //                     )
1423    //                 })?;
1424    //                 match answer.next().await {
1425    //                     Some(0) => save_intent = SaveIntent::SaveAll,
1426    //                     Some(1) => save_intent = SaveIntent::Skip,
1427    //                     _ => {}
1428    //                 }
1429    //             }
1430    //             for (pane, item) in dirty_items {
1431    //                 let (singleton, project_entry_ids) =
1432    //                     cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)));
1433    //                 if singleton || !project_entry_ids.is_empty() {
1434    //                     if let Some(ix) =
1435    //                         pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))?
1436    //                     {
1437    //                         if !Pane::save_item(
1438    //                             project.clone(),
1439    //                             &pane,
1440    //                             ix,
1441    //                             &*item,
1442    //                             save_intent,
1443    //                             &mut cx,
1444    //                         )
1445    //                         .await?
1446    //                         {
1447    //                             return Ok(false);
1448    //                         }
1449    //                     }
1450    //                 }
1451    //             }
1452    //             Ok(true)
1453    //         })
1454    //     }
1455
1456    //     pub fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
1457    //         let mut paths = cx.prompt_for_paths(PathPromptOptions {
1458    //             files: true,
1459    //             directories: true,
1460    //             multiple: true,
1461    //         });
1462
1463    //         Some(cx.spawn(|this, mut cx| async move {
1464    //             if let Some(paths) = paths.recv().await.flatten() {
1465    //                 if let Some(task) = this
1466    //                     .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx))
1467    //                     .log_err()
1468    //                 {
1469    //                     task.await?
1470    //                 }
1471    //             }
1472    //             Ok(())
1473    //         }))
1474    //     }
1475
1476    //     pub fn open_workspace_for_paths(
1477    //         &mut self,
1478    //         paths: Vec<PathBuf>,
1479    //         cx: &mut ViewContext<Self>,
1480    //     ) -> Task<Result<()>> {
1481    //         let window = cx.window().downcast::<Self>();
1482    //         let is_remote = self.project.read(cx).is_remote();
1483    //         let has_worktree = self.project.read(cx).worktrees(cx).next().is_some();
1484    //         let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
1485    //         let close_task = if is_remote || has_worktree || has_dirty_items {
1486    //             None
1487    //         } else {
1488    //             Some(self.prepare_to_close(false, cx))
1489    //         };
1490    //         let app_state = self.app_state.clone();
1491
1492    //         cx.spawn(|_, mut cx| async move {
1493    //             let window_to_replace = if let Some(close_task) = close_task {
1494    //                 if !close_task.await? {
1495    //                     return Ok(());
1496    //                 }
1497    //                 window
1498    //             } else {
1499    //                 None
1500    //             };
1501    //             cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))
1502    //                 .await?;
1503    //             Ok(())
1504    //         })
1505    //     }
1506
1507    #[allow(clippy::type_complexity)]
1508    pub fn open_paths(
1509        &mut self,
1510        mut abs_paths: Vec<PathBuf>,
1511        visible: bool,
1512        cx: &mut ViewContext<Self>,
1513    ) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>> {
1514        log::info!("open paths {abs_paths:?}");
1515
1516        let fs = self.app_state.fs.clone();
1517
1518        // Sort the paths to ensure we add worktrees for parents before their children.
1519        abs_paths.sort_unstable();
1520        cx.spawn(move |this, mut cx| async move {
1521            let mut tasks = Vec::with_capacity(abs_paths.len());
1522            for abs_path in &abs_paths {
1523                let project_path = match this
1524                    .update(&mut cx, |this, cx| {
1525                        Workspace::project_path_for_path(
1526                            this.project.clone(),
1527                            abs_path,
1528                            visible,
1529                            cx,
1530                        )
1531                    })
1532                    .log_err()
1533                {
1534                    Some(project_path) => project_path.await.log_err(),
1535                    None => None,
1536                };
1537
1538                let this = this.clone();
1539                let abs_path = abs_path.clone();
1540                let fs = fs.clone();
1541                let task = cx.spawn(move |mut cx| async move {
1542                    let (worktree, project_path) = project_path?;
1543                    if fs.is_file(&abs_path).await {
1544                        Some(
1545                            this.update(&mut cx, |this, cx| {
1546                                this.open_path(project_path, None, true, cx)
1547                            })
1548                            .log_err()?
1549                            .await,
1550                        )
1551                    } else {
1552                        this.update(&mut cx, |workspace, cx| {
1553                            let worktree = worktree.read(cx);
1554                            let worktree_abs_path = worktree.abs_path();
1555                            let entry_id = if abs_path == worktree_abs_path.as_ref() {
1556                                worktree.root_entry()
1557                            } else {
1558                                abs_path
1559                                    .strip_prefix(worktree_abs_path.as_ref())
1560                                    .ok()
1561                                    .and_then(|relative_path| {
1562                                        worktree.entry_for_path(relative_path)
1563                                    })
1564                            }
1565                            .map(|entry| entry.id);
1566                            if let Some(entry_id) = entry_id {
1567                                workspace.project.update(cx, |_, cx| {
1568                                    cx.emit(project2::Event::ActiveEntryChanged(Some(entry_id)));
1569                                })
1570                            }
1571                        })
1572                        .log_err()?;
1573                        None
1574                    }
1575                });
1576                tasks.push(task);
1577            }
1578
1579            futures::future::join_all(tasks).await
1580        })
1581    }
1582
1583    //     fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext<Self>) {
1584    //         let mut paths = cx.prompt_for_paths(PathPromptOptions {
1585    //             files: false,
1586    //             directories: true,
1587    //             multiple: true,
1588    //         });
1589    //         cx.spawn(|this, mut cx| async move {
1590    //             if let Some(paths) = paths.recv().await.flatten() {
1591    //                 let results = this
1592    //                     .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))?
1593    //                     .await;
1594    //                 for result in results.into_iter().flatten() {
1595    //                     result.log_err();
1596    //                 }
1597    //             }
1598    //             anyhow::Ok(())
1599    //         })
1600    //         .detach_and_log_err(cx);
1601    //     }
1602
1603    fn project_path_for_path(
1604        project: Model<Project>,
1605        abs_path: &Path,
1606        visible: bool,
1607        cx: &mut AppContext,
1608    ) -> Task<Result<(Model<Worktree>, ProjectPath)>> {
1609        let entry = project.update(cx, |project, cx| {
1610            project.find_or_create_local_worktree(abs_path, visible, cx)
1611        });
1612        cx.spawn(|mut cx| async move {
1613            let (worktree, path) = entry.await?;
1614            let worktree_id = worktree.update(&mut cx, |t, _| t.id())?;
1615            Ok((
1616                worktree,
1617                ProjectPath {
1618                    worktree_id,
1619                    path: path.into(),
1620                },
1621            ))
1622        })
1623    }
1624
1625    pub fn items<'a>(
1626        &'a self,
1627        cx: &'a AppContext,
1628    ) -> impl 'a + Iterator<Item = &Box<dyn ItemHandle>> {
1629        self.panes.iter().flat_map(|pane| pane.read(cx).items())
1630    }
1631
1632    //     pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<View<T>> {
1633    //         self.items_of_type(cx).max_by_key(|item| item.id())
1634    //     }
1635
1636    //     pub fn items_of_type<'a, T: Item>(
1637    //         &'a self,
1638    //         cx: &'a AppContext,
1639    //     ) -> impl 'a + Iterator<Item = View<T>> {
1640    //         self.panes
1641    //             .iter()
1642    //             .flat_map(|pane| pane.read(cx).items_of_type())
1643    //     }
1644
1645    pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
1646        self.active_pane().read(cx).active_item()
1647    }
1648
1649    fn active_project_path(&self, cx: &ViewContext<Self>) -> Option<ProjectPath> {
1650        self.active_item(cx).and_then(|item| item.project_path(cx))
1651    }
1652
1653    pub fn save_active_item(
1654        &mut self,
1655        save_intent: SaveIntent,
1656        cx: &mut ViewContext<Self>,
1657    ) -> Task<Result<()>> {
1658        let project = self.project.clone();
1659        let pane = self.active_pane();
1660        let item_ix = pane.read(cx).active_item_index();
1661        let item = pane.read(cx).active_item();
1662        let pane = pane.downgrade();
1663
1664        cx.spawn(|_, mut cx| async move {
1665            if let Some(item) = item {
1666                Pane::save_item(project, &pane, item_ix, item.as_ref(), save_intent, &mut cx)
1667                    .await
1668                    .map(|_| ())
1669            } else {
1670                Ok(())
1671            }
1672        })
1673    }
1674
1675    //     pub fn close_inactive_items_and_panes(
1676    //         &mut self,
1677    //         _: &CloseInactiveTabsAndPanes,
1678    //         cx: &mut ViewContext<Self>,
1679    //     ) -> Option<Task<Result<()>>> {
1680    //         self.close_all_internal(true, SaveIntent::Close, cx)
1681    //     }
1682
1683    //     pub fn close_all_items_and_panes(
1684    //         &mut self,
1685    //         action: &CloseAllItemsAndPanes,
1686    //         cx: &mut ViewContext<Self>,
1687    //     ) -> Option<Task<Result<()>>> {
1688    //         self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx)
1689    //     }
1690
1691    //     fn close_all_internal(
1692    //         &mut self,
1693    //         retain_active_pane: bool,
1694    //         save_intent: SaveIntent,
1695    //         cx: &mut ViewContext<Self>,
1696    //     ) -> Option<Task<Result<()>>> {
1697    //         let current_pane = self.active_pane();
1698
1699    //         let mut tasks = Vec::new();
1700
1701    //         if retain_active_pane {
1702    //             if let Some(current_pane_close) = current_pane.update(cx, |pane, cx| {
1703    //                 pane.close_inactive_items(&CloseInactiveItems, cx)
1704    //             }) {
1705    //                 tasks.push(current_pane_close);
1706    //             };
1707    //         }
1708
1709    //         for pane in self.panes() {
1710    //             if retain_active_pane && pane.id() == current_pane.id() {
1711    //                 continue;
1712    //             }
1713
1714    //             if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| {
1715    //                 pane.close_all_items(
1716    //                     &CloseAllItems {
1717    //                         save_intent: Some(save_intent),
1718    //                     },
1719    //                     cx,
1720    //                 )
1721    //             }) {
1722    //                 tasks.push(close_pane_items)
1723    //             }
1724    //         }
1725
1726    //         if tasks.is_empty() {
1727    //             None
1728    //         } else {
1729    //             Some(cx.spawn(|_, _| async move {
1730    //                 for task in tasks {
1731    //                     task.await?
1732    //                 }
1733    //                 Ok(())
1734    //             }))
1735    //         }
1736    //     }
1737
1738    //     pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
1739    //         let dock = match dock_side {
1740    //             DockPosition::Left => &self.left_dock,
1741    //             DockPosition::Bottom => &self.bottom_dock,
1742    //             DockPosition::Right => &self.right_dock,
1743    //         };
1744    //         let mut focus_center = false;
1745    //         let mut reveal_dock = false;
1746    //         dock.update(cx, |dock, cx| {
1747    //             let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
1748    //             let was_visible = dock.is_open() && !other_is_zoomed;
1749    //             dock.set_open(!was_visible, cx);
1750
1751    //             if let Some(active_panel) = dock.active_panel() {
1752    //                 if was_visible {
1753    //                     if active_panel.has_focus(cx) {
1754    //                         focus_center = true;
1755    //                     }
1756    //                 } else {
1757    //                     cx.focus(active_panel.as_any());
1758    //                     reveal_dock = true;
1759    //                 }
1760    //             }
1761    //         });
1762
1763    //         if reveal_dock {
1764    //             self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx);
1765    //         }
1766
1767    //         if focus_center {
1768    //             cx.focus_self();
1769    //         }
1770
1771    //         cx.notify();
1772    //         self.serialize_workspace(cx);
1773    //     }
1774
1775    pub fn close_all_docks(&mut self, cx: &mut ViewContext<Self>) {
1776        let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock];
1777
1778        for dock in docks {
1779            dock.update(cx, |dock, cx| {
1780                dock.set_open(false, cx);
1781            });
1782        }
1783
1784        // todo!("focus")
1785        // cx.focus_self();
1786        cx.notify();
1787        self.serialize_workspace(cx);
1788    }
1789
1790    //     /// Transfer focus to the panel of the given type.
1791    //     pub fn focus_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) -> Option<View<T>> {
1792    //         self.focus_or_unfocus_panel::<T>(cx, |_, _| true)?
1793    //             .as_any()
1794    //             .clone()
1795    //             .downcast()
1796    //     }
1797
1798    //     /// Focus the panel of the given type if it isn't already focused. If it is
1799    //     /// already focused, then transfer focus back to the workspace center.
1800    //     pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
1801    //         self.focus_or_unfocus_panel::<T>(cx, |panel, cx| !panel.has_focus(cx));
1802    //     }
1803
1804    //     /// Focus or unfocus the given panel type, depending on the given callback.
1805    //     fn focus_or_unfocus_panel<T: Panel>(
1806    //         &mut self,
1807    //         cx: &mut ViewContext<Self>,
1808    //         should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext<Dock>) -> bool,
1809    //     ) -> Option<Rc<dyn PanelHandle>> {
1810    //         for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
1811    //             if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
1812    //                 let mut focus_center = false;
1813    //                 let mut reveal_dock = false;
1814    //                 let panel = dock.update(cx, |dock, cx| {
1815    //                     dock.activate_panel(panel_index, cx);
1816
1817    //                     let panel = dock.active_panel().cloned();
1818    //                     if let Some(panel) = panel.as_ref() {
1819    //                         if should_focus(&**panel, cx) {
1820    //                             dock.set_open(true, cx);
1821    //                             cx.focus(panel.as_any());
1822    //                             reveal_dock = true;
1823    //                         } else {
1824    //                             // if panel.is_zoomed(cx) {
1825    //                             //     dock.set_open(false, cx);
1826    //                             // }
1827    //                             focus_center = true;
1828    //                         }
1829    //                     }
1830    //                     panel
1831    //                 });
1832
1833    //                 if focus_center {
1834    //                     cx.focus_self();
1835    //                 }
1836
1837    //                 self.serialize_workspace(cx);
1838    //                 cx.notify();
1839    //                 return panel;
1840    //             }
1841    //         }
1842    //         None
1843    //     }
1844
1845    //     pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<View<T>> {
1846    //         for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
1847    //             let dock = dock.read(cx);
1848    //             if let Some(panel) = dock.panel::<T>() {
1849    //                 return Some(panel);
1850    //             }
1851    //         }
1852    //         None
1853    //     }
1854
1855    fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
1856        for pane in &self.panes {
1857            pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
1858        }
1859
1860        self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1861        self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1862        self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx));
1863        self.zoomed = None;
1864        self.zoomed_position = None;
1865
1866        cx.notify();
1867    }
1868
1869    //     #[cfg(any(test, feature = "test-support"))]
1870    //     pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
1871    //         self.zoomed.and_then(|view| view.upgrade(cx))
1872    //     }
1873
1874    fn dismiss_zoomed_items_to_reveal(
1875        &mut self,
1876        dock_to_reveal: Option<DockPosition>,
1877        cx: &mut ViewContext<Self>,
1878    ) {
1879        // If a center pane is zoomed, unzoom it.
1880        for pane in &self.panes {
1881            if pane != &self.active_pane || dock_to_reveal.is_some() {
1882                pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
1883            }
1884        }
1885
1886        // If another dock is zoomed, hide it.
1887        let mut focus_center = false;
1888        for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] {
1889            dock.update(cx, |dock, cx| {
1890                if Some(dock.position()) != dock_to_reveal {
1891                    if let Some(panel) = dock.active_panel() {
1892                        if panel.is_zoomed(cx) {
1893                            focus_center |= panel.has_focus(cx);
1894                            dock.set_open(false, cx);
1895                        }
1896                    }
1897                }
1898            });
1899        }
1900
1901        if focus_center {
1902            cx.focus(&self.focus_handle);
1903        }
1904
1905        if self.zoomed_position != dock_to_reveal {
1906            self.zoomed = None;
1907            self.zoomed_position = None;
1908        }
1909
1910        cx.notify();
1911    }
1912
1913    fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> View<Pane> {
1914        let pane = cx.build_view(|cx| {
1915            Pane::new(
1916                self.weak_handle(),
1917                self.project.clone(),
1918                self.pane_history_timestamp.clone(),
1919                cx,
1920            )
1921        });
1922        cx.subscribe(&pane, Self::handle_pane_event).detach();
1923        self.panes.push(pane.clone());
1924        // todo!()
1925        // cx.focus(&pane);
1926        cx.emit(Event::PaneAdded(pane.clone()));
1927        pane
1928    }
1929
1930    //     pub fn add_item_to_center(
1931    //         &mut self,
1932    //         item: Box<dyn ItemHandle>,
1933    //         cx: &mut ViewContext<Self>,
1934    //     ) -> bool {
1935    //         if let Some(center_pane) = self.last_active_center_pane.clone() {
1936    //             if let Some(center_pane) = center_pane.upgrade(cx) {
1937    //                 center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
1938    //                 true
1939    //             } else {
1940    //                 false
1941    //             }
1942    //         } else {
1943    //             false
1944    //         }
1945    //     }
1946
1947    pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
1948        self.active_pane
1949            .update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
1950    }
1951
1952    pub fn split_item(
1953        &mut self,
1954        split_direction: SplitDirection,
1955        item: Box<dyn ItemHandle>,
1956        cx: &mut ViewContext<Self>,
1957    ) {
1958        let new_pane = self.split_pane(self.active_pane.clone(), split_direction, cx);
1959        new_pane.update(cx, move |new_pane, cx| {
1960            new_pane.add_item(item, true, true, None, cx)
1961        })
1962    }
1963
1964    //     pub fn open_abs_path(
1965    //         &mut self,
1966    //         abs_path: PathBuf,
1967    //         visible: bool,
1968    //         cx: &mut ViewContext<Self>,
1969    //     ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
1970    //         cx.spawn(|workspace, mut cx| async move {
1971    //             let open_paths_task_result = workspace
1972    //                 .update(&mut cx, |workspace, cx| {
1973    //                     workspace.open_paths(vec![abs_path.clone()], visible, cx)
1974    //                 })
1975    //                 .with_context(|| format!("open abs path {abs_path:?} task spawn"))?
1976    //                 .await;
1977    //             anyhow::ensure!(
1978    //                 open_paths_task_result.len() == 1,
1979    //                 "open abs path {abs_path:?} task returned incorrect number of results"
1980    //             );
1981    //             match open_paths_task_result
1982    //                 .into_iter()
1983    //                 .next()
1984    //                 .expect("ensured single task result")
1985    //             {
1986    //                 Some(open_result) => {
1987    //                     open_result.with_context(|| format!("open abs path {abs_path:?} task join"))
1988    //                 }
1989    //                 None => anyhow::bail!("open abs path {abs_path:?} task returned None"),
1990    //             }
1991    //         })
1992    //     }
1993
1994    //     pub fn split_abs_path(
1995    //         &mut self,
1996    //         abs_path: PathBuf,
1997    //         visible: bool,
1998    //         cx: &mut ViewContext<Self>,
1999    //     ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
2000    //         let project_path_task =
2001    //             Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx);
2002    //         cx.spawn(|this, mut cx| async move {
2003    //             let (_, path) = project_path_task.await?;
2004    //             this.update(&mut cx, |this, cx| this.split_path(path, cx))?
2005    //                 .await
2006    //         })
2007    //     }
2008
2009    pub fn open_path(
2010        &mut self,
2011        path: impl Into<ProjectPath>,
2012        pane: Option<WeakView<Pane>>,
2013        focus_item: bool,
2014        cx: &mut ViewContext<Self>,
2015    ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
2016        let pane = pane.unwrap_or_else(|| {
2017            self.last_active_center_pane.clone().unwrap_or_else(|| {
2018                self.panes
2019                    .first()
2020                    .expect("There must be an active pane")
2021                    .downgrade()
2022            })
2023        });
2024
2025        let task = self.load_path(path.into(), cx);
2026        cx.spawn(move |_, mut cx| async move {
2027            let (project_entry_id, build_item) = task.await?;
2028            pane.update(&mut cx, |pane, cx| {
2029                pane.open_item(project_entry_id, focus_item, cx, build_item)
2030            })
2031        })
2032    }
2033
2034    //     pub fn split_path(
2035    //         &mut self,
2036    //         path: impl Into<ProjectPath>,
2037    //         cx: &mut ViewContext<Self>,
2038    //     ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
2039    //         let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
2040    //             self.panes
2041    //                 .first()
2042    //                 .expect("There must be an active pane")
2043    //                 .downgrade()
2044    //         });
2045
2046    //         if let Member::Pane(center_pane) = &self.center.root {
2047    //             if center_pane.read(cx).items_len() == 0 {
2048    //                 return self.open_path(path, Some(pane), true, cx);
2049    //             }
2050    //         }
2051
2052    //         let task = self.load_path(path.into(), cx);
2053    //         cx.spawn(|this, mut cx| async move {
2054    //             let (project_entry_id, build_item) = task.await?;
2055    //             this.update(&mut cx, move |this, cx| -> Option<_> {
2056    //                 let pane = pane.upgrade(cx)?;
2057    //                 let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
2058    //                 new_pane.update(cx, |new_pane, cx| {
2059    //                     Some(new_pane.open_item(project_entry_id, true, cx, build_item))
2060    //                 })
2061    //             })
2062    //             .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?
2063    //         })
2064    //     }
2065
2066    pub(crate) fn load_path(
2067        &mut self,
2068        path: ProjectPath,
2069        cx: &mut ViewContext<Self>,
2070    ) -> Task<
2071        Result<(
2072            ProjectEntryId,
2073            impl 'static + Send + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
2074        )>,
2075    > {
2076        let project = self.project().clone();
2077        let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
2078        cx.spawn(|_, mut cx| async move {
2079            let (project_entry_id, project_item) = project_item.await?;
2080            let build_item = cx.update(|_, cx| {
2081                cx.default_global::<ProjectItemBuilders>()
2082                    .get(&project_item.entity_type())
2083                    .ok_or_else(|| anyhow!("no item builder for project item"))
2084                    .cloned()
2085            })??;
2086            let build_item =
2087                move |cx: &mut ViewContext<Pane>| build_item(project, project_item, cx);
2088            Ok((project_entry_id, build_item))
2089        })
2090    }
2091
2092    pub fn open_project_item<T>(
2093        &mut self,
2094        project_item: Model<T::Item>,
2095        cx: &mut ViewContext<Self>,
2096    ) -> View<T>
2097    where
2098        T: ProjectItem,
2099    {
2100        use project2::Item as _;
2101
2102        let entry_id = project_item.read(cx).entry_id(cx);
2103        if let Some(item) = entry_id
2104            .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
2105            .and_then(|item| item.downcast())
2106        {
2107            self.activate_item(&item, cx);
2108            return item;
2109        }
2110
2111        let item =
2112            cx.build_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
2113        self.add_item(Box::new(item.clone()), cx);
2114        item
2115    }
2116
2117    pub fn split_project_item<T>(
2118        &mut self,
2119        project_item: Model<T::Item>,
2120        cx: &mut ViewContext<Self>,
2121    ) -> View<T>
2122    where
2123        T: ProjectItem,
2124    {
2125        use project2::Item as _;
2126
2127        let entry_id = project_item.read(cx).entry_id(cx);
2128        if let Some(item) = entry_id
2129            .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
2130            .and_then(|item| item.downcast())
2131        {
2132            self.activate_item(&item, cx);
2133            return item;
2134        }
2135
2136        let item =
2137            cx.build_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
2138        self.split_item(SplitDirection::Right, Box::new(item.clone()), cx);
2139        item
2140    }
2141
2142    //     pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
2143    //         if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) {
2144    //             self.active_pane.update(cx, |pane, cx| {
2145    //                 pane.add_item(Box::new(shared_screen), false, true, None, cx)
2146    //             });
2147    //         }
2148    //     }
2149
2150    pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
2151        let result = self.panes.iter().find_map(|pane| {
2152            pane.read(cx)
2153                .index_for_item(item)
2154                .map(|ix| (pane.clone(), ix))
2155        });
2156        if let Some((pane, ix)) = result {
2157            pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, cx));
2158            true
2159        } else {
2160            false
2161        }
2162    }
2163
2164    //     fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext<Self>) {
2165    //         let panes = self.center.panes();
2166    //         if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
2167    //             cx.focus(&pane);
2168    //         } else {
2169    //             self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx);
2170    //         }
2171    //     }
2172
2173    //     pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
2174    //         let panes = self.center.panes();
2175    //         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
2176    //             let next_ix = (ix + 1) % panes.len();
2177    //             let next_pane = panes[next_ix].clone();
2178    //             cx.focus(&next_pane);
2179    //         }
2180    //     }
2181
2182    //     pub fn activate_previous_pane(&mut self, cx: &mut ViewContext<Self>) {
2183    //         let panes = self.center.panes();
2184    //         if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
2185    //             let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
2186    //             let prev_pane = panes[prev_ix].clone();
2187    //             cx.focus(&prev_pane);
2188    //         }
2189    //     }
2190
2191    //     pub fn activate_pane_in_direction(
2192    //         &mut self,
2193    //         direction: SplitDirection,
2194    //         cx: &mut ViewContext<Self>,
2195    //     ) {
2196    //         if let Some(pane) = self.find_pane_in_direction(direction, cx) {
2197    //             cx.focus(pane);
2198    //         }
2199    //     }
2200
2201    //     pub fn swap_pane_in_direction(
2202    //         &mut self,
2203    //         direction: SplitDirection,
2204    //         cx: &mut ViewContext<Self>,
2205    //     ) {
2206    //         if let Some(to) = self
2207    //             .find_pane_in_direction(direction, cx)
2208    //             .map(|pane| pane.clone())
2209    //         {
2210    //             self.center.swap(&self.active_pane.clone(), &to);
2211    //             cx.notify();
2212    //         }
2213    //     }
2214
2215    //     fn find_pane_in_direction(
2216    //         &mut self,
2217    //         direction: SplitDirection,
2218    //         cx: &mut ViewContext<Self>,
2219    //     ) -> Option<&View<Pane>> {
2220    //         let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else {
2221    //             return None;
2222    //         };
2223    //         let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx);
2224    //         let center = match cursor {
2225    //             Some(cursor) if bounding_box.contains_point(cursor) => cursor,
2226    //             _ => bounding_box.center(),
2227    //         };
2228
2229    //         let distance_to_next = theme::current(cx).workspace.pane_divider.width + 1.;
2230
2231    //         let target = match direction {
2232    //             SplitDirection::Left => vec2f(bounding_box.origin_x() - distance_to_next, center.y()),
2233    //             SplitDirection::Right => vec2f(bounding_box.max_x() + distance_to_next, center.y()),
2234    //             SplitDirection::Up => vec2f(center.x(), bounding_box.origin_y() - distance_to_next),
2235    //             SplitDirection::Down => vec2f(center.x(), bounding_box.max_y() + distance_to_next),
2236    //         };
2237    //         self.center.pane_at_pixel_position(target)
2238    //     }
2239
2240    fn handle_pane_focused(&mut self, pane: View<Pane>, cx: &mut ViewContext<Self>) {
2241        if self.active_pane != pane {
2242            self.active_pane = pane.clone();
2243            self.status_bar.update(cx, |status_bar, cx| {
2244                status_bar.set_active_pane(&self.active_pane, cx);
2245            });
2246            self.active_item_path_changed(cx);
2247            self.last_active_center_pane = Some(pane.downgrade());
2248        }
2249
2250        self.dismiss_zoomed_items_to_reveal(None, cx);
2251        if pane.read(cx).is_zoomed() {
2252            self.zoomed = Some(pane.downgrade().into());
2253        } else {
2254            self.zoomed = None;
2255        }
2256        self.zoomed_position = None;
2257        self.update_active_view_for_followers(cx);
2258
2259        cx.notify();
2260    }
2261
2262    fn handle_pane_event(
2263        &mut self,
2264        pane: View<Pane>,
2265        event: &pane::Event,
2266        cx: &mut ViewContext<Self>,
2267    ) {
2268        match event {
2269            pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx),
2270            pane::Event::Split(direction) => {
2271                self.split_and_clone(pane, *direction, cx);
2272            }
2273            pane::Event::Remove => self.remove_pane(pane, cx),
2274            pane::Event::ActivateItem { local } => {
2275                if *local {
2276                    self.unfollow(&pane, cx);
2277                }
2278                if &pane == self.active_pane() {
2279                    self.active_item_path_changed(cx);
2280                }
2281            }
2282            pane::Event::ChangeItemTitle => {
2283                if pane == self.active_pane {
2284                    self.active_item_path_changed(cx);
2285                }
2286                self.update_window_edited(cx);
2287            }
2288            pane::Event::RemoveItem { item_id } => {
2289                self.update_window_edited(cx);
2290                if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) {
2291                    if entry.get().entity_id() == pane.entity_id() {
2292                        entry.remove();
2293                    }
2294                }
2295            }
2296            pane::Event::Focus => {
2297                self.handle_pane_focused(pane.clone(), cx);
2298            }
2299            pane::Event::ZoomIn => {
2300                if pane == self.active_pane {
2301                    pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
2302                    if pane.read(cx).has_focus(cx) {
2303                        self.zoomed = Some(pane.downgrade().into());
2304                        self.zoomed_position = None;
2305                    }
2306                    cx.notify();
2307                }
2308            }
2309            pane::Event::ZoomOut => {
2310                pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
2311                if self.zoomed_position.is_none() {
2312                    self.zoomed = None;
2313                }
2314                cx.notify();
2315            }
2316        }
2317
2318        self.serialize_workspace(cx);
2319    }
2320
2321    pub fn split_pane(
2322        &mut self,
2323        pane_to_split: View<Pane>,
2324        split_direction: SplitDirection,
2325        cx: &mut ViewContext<Self>,
2326    ) -> View<Pane> {
2327        let new_pane = self.add_pane(cx);
2328        self.center
2329            .split(&pane_to_split, &new_pane, split_direction)
2330            .unwrap();
2331        cx.notify();
2332        new_pane
2333    }
2334
2335    pub fn split_and_clone(
2336        &mut self,
2337        pane: View<Pane>,
2338        direction: SplitDirection,
2339        cx: &mut ViewContext<Self>,
2340    ) -> Option<View<Pane>> {
2341        let item = pane.read(cx).active_item()?;
2342        let maybe_pane_handle = if let Some(clone) = item.clone_on_split(self.database_id(), cx) {
2343            let new_pane = self.add_pane(cx);
2344            new_pane.update(cx, |pane, cx| pane.add_item(clone, true, true, None, cx));
2345            self.center.split(&pane, &new_pane, direction).unwrap();
2346            Some(new_pane)
2347        } else {
2348            None
2349        };
2350        cx.notify();
2351        maybe_pane_handle
2352    }
2353
2354    pub fn split_pane_with_item(
2355        &mut self,
2356        pane_to_split: WeakView<Pane>,
2357        split_direction: SplitDirection,
2358        from: WeakView<Pane>,
2359        item_id_to_move: EntityId,
2360        cx: &mut ViewContext<Self>,
2361    ) {
2362        let Some(pane_to_split) = pane_to_split.upgrade() else {
2363            return;
2364        };
2365        let Some(from) = from.upgrade() else {
2366            return;
2367        };
2368
2369        let new_pane = self.add_pane(cx);
2370        self.move_item(from.clone(), new_pane.clone(), item_id_to_move, 0, cx);
2371        self.center
2372            .split(&pane_to_split, &new_pane, split_direction)
2373            .unwrap();
2374        cx.notify();
2375    }
2376
2377    pub fn split_pane_with_project_entry(
2378        &mut self,
2379        pane_to_split: WeakView<Pane>,
2380        split_direction: SplitDirection,
2381        project_entry: ProjectEntryId,
2382        cx: &mut ViewContext<Self>,
2383    ) -> Option<Task<Result<()>>> {
2384        let pane_to_split = pane_to_split.upgrade()?;
2385        let new_pane = self.add_pane(cx);
2386        self.center
2387            .split(&pane_to_split, &new_pane, split_direction)
2388            .unwrap();
2389
2390        let path = self.project.read(cx).path_for_entry(project_entry, cx)?;
2391        let task = self.open_path(path, Some(new_pane.downgrade()), true, cx);
2392        Some(cx.foreground_executor().spawn(async move {
2393            task.await?;
2394            Ok(())
2395        }))
2396    }
2397
2398    pub fn move_item(
2399        &mut self,
2400        source: View<Pane>,
2401        destination: View<Pane>,
2402        item_id_to_move: EntityId,
2403        destination_index: usize,
2404        cx: &mut ViewContext<Self>,
2405    ) {
2406        let item_to_move = source
2407            .read(cx)
2408            .items()
2409            .enumerate()
2410            .find(|(_, item_handle)| item_handle.id() == item_id_to_move);
2411
2412        if item_to_move.is_none() {
2413            log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop");
2414            return;
2415        }
2416        let (item_ix, item_handle) = item_to_move.unwrap();
2417        let item_handle = item_handle.clone();
2418
2419        if source != destination {
2420            // Close item from previous pane
2421            source.update(cx, |source, cx| {
2422                source.remove_item(item_ix, false, cx);
2423            });
2424        }
2425
2426        // This automatically removes duplicate items in the pane
2427        destination.update(cx, |destination, cx| {
2428            destination.add_item(item_handle, true, true, Some(destination_index), cx);
2429            destination.focus(cx)
2430        });
2431    }
2432
2433    fn remove_pane(&mut self, pane: View<Pane>, cx: &mut ViewContext<Self>) {
2434        if self.center.remove(&pane).unwrap() {
2435            self.force_remove_pane(&pane, cx);
2436            self.unfollow(&pane, cx);
2437            self.last_leaders_by_pane.remove(&pane.downgrade());
2438            for removed_item in pane.read(cx).items() {
2439                self.panes_by_item.remove(&removed_item.id());
2440            }
2441
2442            cx.notify();
2443        } else {
2444            self.active_item_path_changed(cx);
2445        }
2446    }
2447
2448    pub fn panes(&self) -> &[View<Pane>] {
2449        &self.panes
2450    }
2451
2452    pub fn active_pane(&self) -> &View<Pane> {
2453        &self.active_pane
2454    }
2455
2456    fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
2457        self.follower_states.retain(|_, state| {
2458            if state.leader_id == peer_id {
2459                for item in state.items_by_leader_view_id.values() {
2460                    item.set_leader_peer_id(None, cx);
2461                }
2462                false
2463            } else {
2464                true
2465            }
2466        });
2467        cx.notify();
2468    }
2469
2470    //     fn start_following(
2471    //         &mut self,
2472    //         leader_id: PeerId,
2473    //         cx: &mut ViewContext<Self>,
2474    //     ) -> Option<Task<Result<()>>> {
2475    //         let pane = self.active_pane().clone();
2476
2477    //         self.last_leaders_by_pane
2478    //             .insert(pane.downgrade(), leader_id);
2479    //         self.unfollow(&pane, cx);
2480    //         self.follower_states.insert(
2481    //             pane.clone(),
2482    //             FollowerState {
2483    //                 leader_id,
2484    //                 active_view_id: None,
2485    //                 items_by_leader_view_id: Default::default(),
2486    //             },
2487    //         );
2488    //         cx.notify();
2489
2490    //         let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
2491    //         let project_id = self.project.read(cx).remote_id();
2492    //         let request = self.app_state.client.request(proto::Follow {
2493    //             room_id,
2494    //             project_id,
2495    //             leader_id: Some(leader_id),
2496    //         });
2497
2498    //         Some(cx.spawn(|this, mut cx| async move {
2499    //             let response = request.await?;
2500    //             this.update(&mut cx, |this, _| {
2501    //                 let state = this
2502    //                     .follower_states
2503    //                     .get_mut(&pane)
2504    //                     .ok_or_else(|| anyhow!("following interrupted"))?;
2505    //                 state.active_view_id = if let Some(active_view_id) = response.active_view_id {
2506    //                     Some(ViewId::from_proto(active_view_id)?)
2507    //                 } else {
2508    //                     None
2509    //                 };
2510    //                 Ok::<_, anyhow::Error>(())
2511    //             })??;
2512    //             Self::add_views_from_leader(
2513    //                 this.clone(),
2514    //                 leader_id,
2515    //                 vec![pane],
2516    //                 response.views,
2517    //                 &mut cx,
2518    //             )
2519    //             .await?;
2520    //             this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?;
2521    //             Ok(())
2522    //         }))
2523    //     }
2524
2525    //     pub fn follow_next_collaborator(
2526    //         &mut self,
2527    //         _: &FollowNextCollaborator,
2528    //         cx: &mut ViewContext<Self>,
2529    //     ) -> Option<Task<Result<()>>> {
2530    //         let collaborators = self.project.read(cx).collaborators();
2531    //         let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
2532    //             let mut collaborators = collaborators.keys().copied();
2533    //             for peer_id in collaborators.by_ref() {
2534    //                 if peer_id == leader_id {
2535    //                     break;
2536    //                 }
2537    //             }
2538    //             collaborators.next()
2539    //         } else if let Some(last_leader_id) =
2540    //             self.last_leaders_by_pane.get(&self.active_pane.downgrade())
2541    //         {
2542    //             if collaborators.contains_key(last_leader_id) {
2543    //                 Some(*last_leader_id)
2544    //             } else {
2545    //                 None
2546    //             }
2547    //         } else {
2548    //             None
2549    //         };
2550
2551    //         let pane = self.active_pane.clone();
2552    //         let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
2553    //         else {
2554    //             return None;
2555    //         };
2556    //         if Some(leader_id) == self.unfollow(&pane, cx) {
2557    //             return None;
2558    //         }
2559    //         self.follow(leader_id, cx)
2560    //     }
2561
2562    //     pub fn follow(
2563    //         &mut self,
2564    //         leader_id: PeerId,
2565    //         cx: &mut ViewContext<Self>,
2566    //     ) -> Option<Task<Result<()>>> {
2567    //         let room = ActiveCall::global(cx).read(cx).room()?.read(cx);
2568    //         let project = self.project.read(cx);
2569
2570    //         let Some(remote_participant) = room.remote_participant_for_peer_id(leader_id) else {
2571    //             return None;
2572    //         };
2573
2574    //         let other_project_id = match remote_participant.location {
2575    //             call::ParticipantLocation::External => None,
2576    //             call::ParticipantLocation::UnsharedProject => None,
2577    //             call::ParticipantLocation::SharedProject { project_id } => {
2578    //                 if Some(project_id) == project.remote_id() {
2579    //                     None
2580    //                 } else {
2581    //                     Some(project_id)
2582    //                 }
2583    //             }
2584    //         };
2585
2586    //         // if they are active in another project, follow there.
2587    //         if let Some(project_id) = other_project_id {
2588    //             let app_state = self.app_state.clone();
2589    //             return Some(crate::join_remote_project(
2590    //                 project_id,
2591    //                 remote_participant.user.id,
2592    //                 app_state,
2593    //                 cx,
2594    //             ));
2595    //         }
2596
2597    //         // if you're already following, find the right pane and focus it.
2598    //         for (pane, state) in &self.follower_states {
2599    //             if leader_id == state.leader_id {
2600    //                 cx.focus(pane);
2601    //                 return None;
2602    //             }
2603    //         }
2604
2605    //         // Otherwise, follow.
2606    //         self.start_following(leader_id, cx)
2607    //     }
2608
2609    pub fn unfollow(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Option<PeerId> {
2610        let state = self.follower_states.remove(pane)?;
2611        let leader_id = state.leader_id;
2612        for (_, item) in state.items_by_leader_view_id {
2613            item.set_leader_peer_id(None, cx);
2614        }
2615
2616        if self
2617            .follower_states
2618            .values()
2619            .all(|state| state.leader_id != state.leader_id)
2620        {
2621            let project_id = self.project.read(cx).remote_id();
2622            let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
2623            self.app_state
2624                .client
2625                .send(proto::Unfollow {
2626                    room_id,
2627                    project_id,
2628                    leader_id: Some(leader_id),
2629                })
2630                .log_err();
2631        }
2632
2633        cx.notify();
2634        Some(leader_id)
2635    }
2636
2637    //     pub fn is_being_followed(&self, peer_id: PeerId) -> bool {
2638    //         self.follower_states
2639    //             .values()
2640    //             .any(|state| state.leader_id == peer_id)
2641    //     }
2642
2643    fn render_titlebar(&self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
2644        h_stack()
2645            .id("titlebar")
2646            .justify_between()
2647            .when(
2648                !matches!(cx.window_bounds(), WindowBounds::Fullscreen),
2649                |s| s.pl_20(),
2650            )
2651            .w_full()
2652            .h(rems(1.75))
2653            .bg(cx.theme().colors().title_bar_background)
2654            .on_click(|_, event, cx| {
2655                if event.up.click_count == 2 {
2656                    cx.zoom_window();
2657                }
2658            })
2659            .child(
2660                h_stack()
2661                    // TODO - Add player menu
2662                    .child(
2663                        Button::new("player")
2664                            .variant(ButtonVariant::Ghost)
2665                            .color(Some(LabelColor::Player(0))),
2666                    )
2667                    // TODO - Add project menu
2668                    .child(Button::new("project_name").variant(ButtonVariant::Ghost))
2669                    // TODO - Add git menu
2670                    .child(
2671                        Button::new("branch_name")
2672                            .variant(ButtonVariant::Ghost)
2673                            .color(Some(LabelColor::Muted)),
2674                    ),
2675            ) // self.titlebar_item
2676            .child(h_stack().child(Label::new("Right side titlebar item")))
2677    }
2678
2679    fn active_item_path_changed(&mut self, cx: &mut ViewContext<Self>) {
2680        let active_entry = self.active_project_path(cx);
2681        self.project
2682            .update(cx, |project, cx| project.set_active_path(active_entry, cx));
2683        self.update_window_title(cx);
2684    }
2685
2686    fn update_window_title(&mut self, cx: &mut ViewContext<Self>) {
2687        let project = self.project().read(cx);
2688        let mut title = String::new();
2689
2690        if let Some(path) = self.active_item(cx).and_then(|item| item.project_path(cx)) {
2691            let filename = path
2692                .path
2693                .file_name()
2694                .map(|s| s.to_string_lossy())
2695                .or_else(|| {
2696                    Some(Cow::Borrowed(
2697                        project
2698                            .worktree_for_id(path.worktree_id, cx)?
2699                            .read(cx)
2700                            .root_name(),
2701                    ))
2702                });
2703
2704            if let Some(filename) = filename {
2705                title.push_str(filename.as_ref());
2706                title.push_str(" β€” ");
2707            }
2708        }
2709
2710        for (i, name) in project.worktree_root_names(cx).enumerate() {
2711            if i > 0 {
2712                title.push_str(", ");
2713            }
2714            title.push_str(name);
2715        }
2716
2717        if title.is_empty() {
2718            title = "empty project".to_string();
2719        }
2720
2721        if project.is_remote() {
2722            title.push_str(" ↙");
2723        } else if project.is_shared() {
2724            title.push_str(" β†—");
2725        }
2726
2727        // todo!()
2728        // cx.set_window_title(&title);
2729    }
2730
2731    fn update_window_edited(&mut self, cx: &mut ViewContext<Self>) {
2732        let is_edited = !self.project.read(cx).is_read_only()
2733            && self
2734                .items(cx)
2735                .any(|item| item.has_conflict(cx) || item.is_dirty(cx));
2736        if is_edited != self.window_edited {
2737            self.window_edited = is_edited;
2738            // todo!()
2739            // cx.set_window_edited(self.window_edited)
2740        }
2741    }
2742
2743    //     fn render_disconnected_overlay(
2744    //         &self,
2745    //         cx: &mut ViewContext<Workspace>,
2746    //     ) -> Option<AnyElement<Workspace>> {
2747    //         if self.project.read(cx).is_read_only() {
2748    //             enum DisconnectedOverlay {}
2749    //             Some(
2750    //                 MouseEventHandler::new::<DisconnectedOverlay, _>(0, cx, |_, cx| {
2751    //                     let theme = &theme::current(cx);
2752    //                     Label::new(
2753    //                         "Your connection to the remote project has been lost.",
2754    //                         theme.workspace.disconnected_overlay.text.clone(),
2755    //                     )
2756    //                     .aligned()
2757    //                     .contained()
2758    //                     .with_style(theme.workspace.disconnected_overlay.container)
2759    //                 })
2760    //                 .with_cursor_style(CursorStyle::Arrow)
2761    //                 .capture_all()
2762    //                 .into_any_named("disconnected overlay"),
2763    //             )
2764    //         } else {
2765    //             None
2766    //         }
2767    //     }
2768
2769    //     fn render_notifications(
2770    //         &self,
2771    //         theme: &theme::Workspace,
2772    //         cx: &AppContext,
2773    //     ) -> Option<AnyElement<Workspace>> {
2774    //         if self.notifications.is_empty() {
2775    //             None
2776    //         } else {
2777    //             Some(
2778    //                 Flex::column()
2779    //                     .with_children(self.notifications.iter().map(|(_, _, notification)| {
2780    //                         ChildView::new(notification.as_any(), cx)
2781    //                             .contained()
2782    //                             .with_style(theme.notification)
2783    //                     }))
2784    //                     .constrained()
2785    //                     .with_width(theme.notifications.width)
2786    //                     .contained()
2787    //                     .with_style(theme.notifications.container)
2788    //                     .aligned()
2789    //                     .bottom()
2790    //                     .right()
2791    //                     .into_any(),
2792    //             )
2793    //         }
2794    //     }
2795
2796    //     // RPC handlers
2797
2798    fn handle_follow(
2799        &mut self,
2800        _follower_project_id: Option<u64>,
2801        _cx: &mut ViewContext<Self>,
2802    ) -> proto::FollowResponse {
2803        todo!()
2804
2805        //     let client = &self.app_state.client;
2806        //     let project_id = self.project.read(cx).remote_id();
2807
2808        //     let active_view_id = self.active_item(cx).and_then(|i| {
2809        //         Some(
2810        //             i.to_followable_item_handle(cx)?
2811        //                 .remote_id(client, cx)?
2812        //                 .to_proto(),
2813        //         )
2814        //     });
2815
2816        //     cx.notify();
2817
2818        //     self.last_active_view_id = active_view_id.clone();
2819        //     proto::FollowResponse {
2820        //         active_view_id,
2821        //         views: self
2822        //             .panes()
2823        //             .iter()
2824        //             .flat_map(|pane| {
2825        //                 let leader_id = self.leader_for_pane(pane);
2826        //                 pane.read(cx).items().filter_map({
2827        //                     let cx = &cx;
2828        //                     move |item| {
2829        //                         let item = item.to_followable_item_handle(cx)?;
2830        //                         if (project_id.is_none() || project_id != follower_project_id)
2831        //                             && item.is_project_item(cx)
2832        //                         {
2833        //                             return None;
2834        //                         }
2835        //                         let id = item.remote_id(client, cx)?.to_proto();
2836        //                         let variant = item.to_state_proto(cx)?;
2837        //                         Some(proto::View {
2838        //                             id: Some(id),
2839        //                             leader_id,
2840        //                             variant: Some(variant),
2841        //                         })
2842        //                     }
2843        //                 })
2844        //             })
2845        //             .collect(),
2846        //     }
2847    }
2848
2849    fn handle_update_followers(
2850        &mut self,
2851        leader_id: PeerId,
2852        message: proto::UpdateFollowers,
2853        _cx: &mut ViewContext<Self>,
2854    ) {
2855        self.leader_updates_tx
2856            .unbounded_send((leader_id, message))
2857            .ok();
2858    }
2859
2860    async fn process_leader_update(
2861        this: &WeakView<Self>,
2862        leader_id: PeerId,
2863        update: proto::UpdateFollowers,
2864        cx: &mut AsyncWindowContext,
2865    ) -> Result<()> {
2866        match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
2867            proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
2868                this.update(cx, |this, _| {
2869                    for (_, state) in &mut this.follower_states {
2870                        if state.leader_id == leader_id {
2871                            state.active_view_id =
2872                                if let Some(active_view_id) = update_active_view.id.clone() {
2873                                    Some(ViewId::from_proto(active_view_id)?)
2874                                } else {
2875                                    None
2876                                };
2877                        }
2878                    }
2879                    anyhow::Ok(())
2880                })??;
2881            }
2882            proto::update_followers::Variant::UpdateView(update_view) => {
2883                let variant = update_view
2884                    .variant
2885                    .ok_or_else(|| anyhow!("missing update view variant"))?;
2886                let id = update_view
2887                    .id
2888                    .ok_or_else(|| anyhow!("missing update view id"))?;
2889                let mut tasks = Vec::new();
2890                this.update(cx, |this, cx| {
2891                    let project = this.project.clone();
2892                    for (_, state) in &mut this.follower_states {
2893                        if state.leader_id == leader_id {
2894                            let view_id = ViewId::from_proto(id.clone())?;
2895                            if let Some(item) = state.items_by_leader_view_id.get(&view_id) {
2896                                tasks.push(item.apply_update_proto(&project, variant.clone(), cx));
2897                            }
2898                        }
2899                    }
2900                    anyhow::Ok(())
2901                })??;
2902                try_join_all(tasks).await.log_err();
2903            }
2904            proto::update_followers::Variant::CreateView(view) => {
2905                let panes = this.update(cx, |this, _| {
2906                    this.follower_states
2907                        .iter()
2908                        .filter_map(|(pane, state)| (state.leader_id == leader_id).then_some(pane))
2909                        .cloned()
2910                        .collect()
2911                })?;
2912                Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?;
2913            }
2914        }
2915        this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?;
2916        Ok(())
2917    }
2918
2919    async fn add_views_from_leader(
2920        this: WeakView<Self>,
2921        leader_id: PeerId,
2922        panes: Vec<View<Pane>>,
2923        views: Vec<proto::View>,
2924        cx: &mut AsyncWindowContext,
2925    ) -> Result<()> {
2926        let this = this.upgrade().context("workspace dropped")?;
2927
2928        let item_builders = cx.update(|_, cx| {
2929            cx.default_global::<FollowableItemBuilders>()
2930                .values()
2931                .map(|b| b.0)
2932                .collect::<Vec<_>>()
2933        })?;
2934
2935        let mut item_tasks_by_pane = HashMap::default();
2936        for pane in panes {
2937            let mut item_tasks = Vec::new();
2938            let mut leader_view_ids = Vec::new();
2939            for view in &views {
2940                let Some(id) = &view.id else { continue };
2941                let id = ViewId::from_proto(id.clone())?;
2942                let mut variant = view.variant.clone();
2943                if variant.is_none() {
2944                    Err(anyhow!("missing view variant"))?;
2945                }
2946                for build_item in &item_builders {
2947                    let task = cx.update(|_, cx| {
2948                        build_item(pane.clone(), this.clone(), id, &mut variant, cx)
2949                    })?;
2950                    if let Some(task) = task {
2951                        item_tasks.push(task);
2952                        leader_view_ids.push(id);
2953                        break;
2954                    } else {
2955                        assert!(variant.is_some());
2956                    }
2957                }
2958            }
2959
2960            item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids));
2961        }
2962
2963        for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane {
2964            let items = futures::future::try_join_all(item_tasks).await?;
2965            this.update(cx, |this, cx| {
2966                let state = this.follower_states.get_mut(&pane)?;
2967                for (id, item) in leader_view_ids.into_iter().zip(items) {
2968                    item.set_leader_peer_id(Some(leader_id), cx);
2969                    state.items_by_leader_view_id.insert(id, item);
2970                }
2971
2972                Some(())
2973            })?;
2974        }
2975        Ok(())
2976    }
2977
2978    fn update_active_view_for_followers(&mut self, cx: &mut ViewContext<Self>) {
2979        let mut is_project_item = true;
2980        let mut update = proto::UpdateActiveView::default();
2981        if self.active_pane.read(cx).has_focus(cx) {
2982            let item = self
2983                .active_item(cx)
2984                .and_then(|item| item.to_followable_item_handle(cx));
2985            if let Some(item) = item {
2986                is_project_item = item.is_project_item(cx);
2987                update = proto::UpdateActiveView {
2988                    id: item
2989                        .remote_id(&self.app_state.client, cx)
2990                        .map(|id| id.to_proto()),
2991                    leader_id: self.leader_for_pane(&self.active_pane),
2992                };
2993            }
2994        }
2995
2996        if update.id != self.last_active_view_id {
2997            self.last_active_view_id = update.id.clone();
2998            self.update_followers(
2999                is_project_item,
3000                proto::update_followers::Variant::UpdateActiveView(update),
3001                cx,
3002            );
3003        }
3004    }
3005
3006    fn update_followers(
3007        &self,
3008        project_only: bool,
3009        update: proto::update_followers::Variant,
3010        cx: &mut WindowContext,
3011    ) -> Option<()> {
3012        let project_id = if project_only {
3013            self.project.read(cx).remote_id()
3014        } else {
3015            None
3016        };
3017        self.app_state().workspace_store.update(cx, |store, cx| {
3018            store.update_followers(project_id, update, cx)
3019        })
3020    }
3021
3022    pub fn leader_for_pane(&self, pane: &View<Pane>) -> Option<PeerId> {
3023        self.follower_states.get(pane).map(|state| state.leader_id)
3024    }
3025
3026    fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
3027        cx.notify();
3028
3029        let call = self.active_call()?;
3030        let room = call.read(cx).room()?.read(cx);
3031        let participant = room.remote_participant_for_peer_id(leader_id)?;
3032        let mut items_to_activate = Vec::new();
3033
3034        let leader_in_this_app;
3035        let leader_in_this_project;
3036        match participant.location {
3037            call2::ParticipantLocation::SharedProject { project_id } => {
3038                leader_in_this_app = true;
3039                leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id();
3040            }
3041            call2::ParticipantLocation::UnsharedProject => {
3042                leader_in_this_app = true;
3043                leader_in_this_project = false;
3044            }
3045            call2::ParticipantLocation::External => {
3046                leader_in_this_app = false;
3047                leader_in_this_project = false;
3048            }
3049        };
3050
3051        for (pane, state) in &self.follower_states {
3052            if state.leader_id != leader_id {
3053                continue;
3054            }
3055            if let (Some(active_view_id), true) = (state.active_view_id, leader_in_this_app) {
3056                if let Some(item) = state.items_by_leader_view_id.get(&active_view_id) {
3057                    if leader_in_this_project || !item.is_project_item(cx) {
3058                        items_to_activate.push((pane.clone(), item.boxed_clone()));
3059                    }
3060                } else {
3061                    log::warn!(
3062                        "unknown view id {:?} for leader {:?}",
3063                        active_view_id,
3064                        leader_id
3065                    );
3066                }
3067                continue;
3068            }
3069            // todo!()
3070            // if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) {
3071            //     items_to_activate.push((pane.clone(), Box::new(shared_screen)));
3072            // }
3073        }
3074
3075        for (pane, item) in items_to_activate {
3076            let pane_was_focused = pane.read(cx).has_focus(cx);
3077            if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) {
3078                pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx));
3079            } else {
3080                pane.update(cx, |pane, cx| {
3081                    pane.add_item(item.boxed_clone(), false, false, None, cx)
3082                });
3083            }
3084
3085            if pane_was_focused {
3086                pane.update(cx, |pane, cx| pane.focus_active_item(cx));
3087            }
3088        }
3089
3090        None
3091    }
3092
3093    // todo!()
3094    //     fn shared_screen_for_peer(
3095    //         &self,
3096    //         peer_id: PeerId,
3097    //         pane: &View<Pane>,
3098    //         cx: &mut ViewContext<Self>,
3099    //     ) -> Option<View<SharedScreen>> {
3100    //         let call = self.active_call()?;
3101    //         let room = call.read(cx).room()?.read(cx);
3102    //         let participant = room.remote_participant_for_peer_id(peer_id)?;
3103    //         let track = participant.video_tracks.values().next()?.clone();
3104    //         let user = participant.user.clone();
3105
3106    //         for item in pane.read(cx).items_of_type::<SharedScreen>() {
3107    //             if item.read(cx).peer_id == peer_id {
3108    //                 return Some(item);
3109    //             }
3110    //         }
3111
3112    //         Some(cx.build_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx)))
3113    //     }
3114
3115    pub fn on_window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
3116        if cx.is_window_active() {
3117            self.update_active_view_for_followers(cx);
3118            cx.background_executor()
3119                .spawn(persistence::DB.update_timestamp(self.database_id()))
3120                .detach();
3121        } else {
3122            for pane in &self.panes {
3123                pane.update(cx, |pane, cx| {
3124                    if let Some(item) = pane.active_item() {
3125                        item.workspace_deactivated(cx);
3126                    }
3127                    if matches!(
3128                        WorkspaceSettings::get_global(cx).autosave,
3129                        AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange
3130                    ) {
3131                        for item in pane.items() {
3132                            Pane::autosave_item(item.as_ref(), self.project.clone(), cx)
3133                                .detach_and_log_err(cx);
3134                        }
3135                    }
3136                });
3137            }
3138        }
3139    }
3140
3141    fn active_call(&self) -> Option<&Model<ActiveCall>> {
3142        self.active_call.as_ref().map(|(call, _)| call)
3143    }
3144
3145    fn on_active_call_event(
3146        &mut self,
3147        _: Model<ActiveCall>,
3148        event: &call2::room::Event,
3149        cx: &mut ViewContext<Self>,
3150    ) {
3151        match event {
3152            call2::room::Event::ParticipantLocationChanged { participant_id }
3153            | call2::room::Event::RemoteVideoTracksChanged { participant_id } => {
3154                self.leader_updated(*participant_id, cx);
3155            }
3156            _ => {}
3157        }
3158    }
3159
3160    pub fn database_id(&self) -> WorkspaceId {
3161        self.database_id
3162    }
3163
3164    fn location(&self, cx: &AppContext) -> Option<WorkspaceLocation> {
3165        let project = self.project().read(cx);
3166
3167        if project.is_local() {
3168            Some(
3169                project
3170                    .visible_worktrees(cx)
3171                    .map(|worktree| worktree.read(cx).abs_path())
3172                    .collect::<Vec<_>>()
3173                    .into(),
3174            )
3175        } else {
3176            None
3177        }
3178    }
3179
3180    fn remove_panes(&mut self, member: Member, cx: &mut ViewContext<Workspace>) {
3181        match member {
3182            Member::Axis(PaneAxis { members, .. }) => {
3183                for child in members.iter() {
3184                    self.remove_panes(child.clone(), cx)
3185                }
3186            }
3187            Member::Pane(pane) => {
3188                self.force_remove_pane(&pane, cx);
3189            }
3190        }
3191    }
3192
3193    fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
3194        self.panes.retain(|p| p != pane);
3195        if true {
3196            todo!()
3197            // cx.focus(self.panes.last().unwrap());
3198        }
3199        if self.last_active_center_pane == Some(pane.downgrade()) {
3200            self.last_active_center_pane = None;
3201        }
3202        cx.notify();
3203    }
3204
3205    //     fn schedule_serialize(&mut self, cx: &mut ViewContext<Self>) {
3206    //         self._schedule_serialize = Some(cx.spawn(|this, cx| async move {
3207    //             cx.background().timer(Duration::from_millis(100)).await;
3208    //             this.read_with(&cx, |this, cx| this.serialize_workspace(cx))
3209    //                 .ok();
3210    //         }));
3211    //     }
3212
3213    fn serialize_workspace(&self, cx: &mut ViewContext<Self>) {
3214        fn serialize_pane_handle(pane_handle: &View<Pane>, cx: &WindowContext) -> SerializedPane {
3215            let (items, active) = {
3216                let pane = pane_handle.read(cx);
3217                let active_item_id = pane.active_item().map(|item| item.id());
3218                (
3219                    pane.items()
3220                        .filter_map(|item_handle| {
3221                            Some(SerializedItem {
3222                                kind: Arc::from(item_handle.serialized_item_kind()?),
3223                                item_id: item_handle.id().as_u64() as usize,
3224                                active: Some(item_handle.id()) == active_item_id,
3225                            })
3226                        })
3227                        .collect::<Vec<_>>(),
3228                    pane.has_focus(cx),
3229                )
3230            };
3231
3232            SerializedPane::new(items, active)
3233        }
3234
3235        fn build_serialized_pane_group(
3236            pane_group: &Member,
3237            cx: &WindowContext,
3238        ) -> SerializedPaneGroup {
3239            match pane_group {
3240                Member::Axis(PaneAxis {
3241                    axis,
3242                    members,
3243                    flexes,
3244                    bounding_boxes: _,
3245                }) => SerializedPaneGroup::Group {
3246                    axis: *axis,
3247                    children: members
3248                        .iter()
3249                        .map(|member| build_serialized_pane_group(member, cx))
3250                        .collect::<Vec<_>>(),
3251                    flexes: Some(flexes.lock().clone()),
3252                },
3253                Member::Pane(pane_handle) => {
3254                    SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
3255                }
3256            }
3257        }
3258
3259        fn build_serialized_docks(
3260            this: &Workspace,
3261            cx: &mut ViewContext<Workspace>,
3262        ) -> DockStructure {
3263            let left_dock = this.left_dock.read(cx);
3264            let left_visible = left_dock.is_open();
3265            let left_active_panel = left_dock
3266                .visible_panel()
3267                .and_then(|panel| Some(panel.persistent_name(cx).to_string()));
3268            let left_dock_zoom = left_dock
3269                .visible_panel()
3270                .map(|panel| panel.is_zoomed(cx))
3271                .unwrap_or(false);
3272
3273            let right_dock = this.right_dock.read(cx);
3274            let right_visible = right_dock.is_open();
3275            let right_active_panel = right_dock
3276                .visible_panel()
3277                .and_then(|panel| Some(panel.persistent_name(cx).to_string()));
3278            let right_dock_zoom = right_dock
3279                .visible_panel()
3280                .map(|panel| panel.is_zoomed(cx))
3281                .unwrap_or(false);
3282
3283            let bottom_dock = this.bottom_dock.read(cx);
3284            let bottom_visible = bottom_dock.is_open();
3285            let bottom_active_panel = bottom_dock
3286                .visible_panel()
3287                .and_then(|panel| Some(panel.persistent_name(cx).to_string()));
3288            let bottom_dock_zoom = bottom_dock
3289                .visible_panel()
3290                .map(|panel| panel.is_zoomed(cx))
3291                .unwrap_or(false);
3292
3293            DockStructure {
3294                left: DockData {
3295                    visible: left_visible,
3296                    active_panel: left_active_panel,
3297                    zoom: left_dock_zoom,
3298                },
3299                right: DockData {
3300                    visible: right_visible,
3301                    active_panel: right_active_panel,
3302                    zoom: right_dock_zoom,
3303                },
3304                bottom: DockData {
3305                    visible: bottom_visible,
3306                    active_panel: bottom_active_panel,
3307                    zoom: bottom_dock_zoom,
3308                },
3309            }
3310        }
3311
3312        if let Some(location) = self.location(cx) {
3313            // Load bearing special case:
3314            //  - with_local_workspace() relies on this to not have other stuff open
3315            //    when you open your log
3316            if !location.paths().is_empty() {
3317                let center_group = build_serialized_pane_group(&self.center.root, cx);
3318                let docks = build_serialized_docks(self, cx);
3319
3320                let serialized_workspace = SerializedWorkspace {
3321                    id: self.database_id,
3322                    location,
3323                    center_group,
3324                    bounds: Default::default(),
3325                    display: Default::default(),
3326                    docks,
3327                };
3328
3329                cx.spawn(|_, _| persistence::DB.save_workspace(serialized_workspace))
3330                    .detach();
3331            }
3332        }
3333    }
3334
3335    pub(crate) fn load_workspace(
3336        serialized_workspace: SerializedWorkspace,
3337        paths_to_open: Vec<Option<ProjectPath>>,
3338        cx: &mut ViewContext<Workspace>,
3339    ) -> Task<Result<Vec<Option<Box<dyn ItemHandle>>>>> {
3340        cx.spawn(|workspace, mut cx| async move {
3341            let (project, old_center_pane) = workspace.update(&mut cx, |workspace, _| {
3342                (
3343                    workspace.project().clone(),
3344                    workspace.last_active_center_pane.clone(),
3345                )
3346            })?;
3347
3348            let mut center_group = None;
3349            let mut center_items = None;
3350
3351            // Traverse the splits tree and add to things
3352            if let Some((group, active_pane, items)) = serialized_workspace
3353                .center_group
3354                .deserialize(
3355                    &project,
3356                    serialized_workspace.id,
3357                    workspace.clone(),
3358                    &mut cx,
3359                )
3360                .await
3361            {
3362                center_items = Some(items);
3363                center_group = Some((group, active_pane))
3364            }
3365
3366            let mut items_by_project_path = cx.update(|_, cx| {
3367                center_items
3368                    .unwrap_or_default()
3369                    .into_iter()
3370                    .filter_map(|item| {
3371                        let item = item?;
3372                        let project_path = item.project_path(cx)?;
3373                        Some((project_path, item))
3374                    })
3375                    .collect::<HashMap<_, _>>()
3376            })?;
3377
3378            let opened_items = paths_to_open
3379                .into_iter()
3380                .map(|path_to_open| {
3381                    path_to_open
3382                        .and_then(|path_to_open| items_by_project_path.remove(&path_to_open))
3383                })
3384                .collect::<Vec<_>>();
3385
3386            // Remove old panes from workspace panes list
3387            workspace.update(&mut cx, |workspace, cx| {
3388                if let Some((center_group, active_pane)) = center_group {
3389                    workspace.remove_panes(workspace.center.root.clone(), cx);
3390
3391                    // Swap workspace center group
3392                    workspace.center = PaneGroup::with_root(center_group);
3393
3394                    // Change the focus to the workspace first so that we retrigger focus in on the pane.
3395                    // todo!()
3396                    // cx.focus_self();
3397                    // if let Some(active_pane) = active_pane {
3398                    //     cx.focus(&active_pane);
3399                    // } else {
3400                    //     cx.focus(workspace.panes.last().unwrap());
3401                    // }
3402                } else {
3403                    // todo!()
3404                    // let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade());
3405                    // if let Some(old_center_handle) = old_center_handle {
3406                    //     cx.focus(&old_center_handle)
3407                    // } else {
3408                    //     cx.focus_self()
3409                    // }
3410                }
3411
3412                let docks = serialized_workspace.docks;
3413                workspace.left_dock.update(cx, |dock, cx| {
3414                    dock.set_open(docks.left.visible, cx);
3415                    if let Some(active_panel) = docks.left.active_panel {
3416                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3417                            dock.activate_panel(ix, cx);
3418                        }
3419                    }
3420                    dock.active_panel()
3421                        .map(|panel| panel.set_zoomed(docks.left.zoom, cx));
3422                    if docks.left.visible && docks.left.zoom {
3423                        // todo!()
3424                        // cx.focus_self()
3425                    }
3426                });
3427                // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
3428                workspace.right_dock.update(cx, |dock, cx| {
3429                    dock.set_open(docks.right.visible, cx);
3430                    if let Some(active_panel) = docks.right.active_panel {
3431                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3432                            dock.activate_panel(ix, cx);
3433                        }
3434                    }
3435                    dock.active_panel()
3436                        .map(|panel| panel.set_zoomed(docks.right.zoom, cx));
3437
3438                    if docks.right.visible && docks.right.zoom {
3439                        // todo!()
3440                        // cx.focus_self()
3441                    }
3442                });
3443                workspace.bottom_dock.update(cx, |dock, cx| {
3444                    dock.set_open(docks.bottom.visible, cx);
3445                    if let Some(active_panel) = docks.bottom.active_panel {
3446                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
3447                            dock.activate_panel(ix, cx);
3448                        }
3449                    }
3450
3451                    dock.active_panel()
3452                        .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx));
3453
3454                    if docks.bottom.visible && docks.bottom.zoom {
3455                        // todo!()
3456                        // cx.focus_self()
3457                    }
3458                });
3459
3460                cx.notify();
3461            })?;
3462
3463            // Serialize ourself to make sure our timestamps and any pane / item changes are replicated
3464            workspace.update(&mut cx, |workspace, cx| workspace.serialize_workspace(cx))?;
3465
3466            Ok(opened_items)
3467        })
3468    }
3469
3470    #[cfg(any(test, feature = "test-support"))]
3471    pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
3472        use gpui::Context;
3473        use node_runtime::FakeNodeRuntime;
3474
3475        let client = project.read(cx).client();
3476        let user_store = project.read(cx).user_store();
3477
3478        let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
3479        let app_state = Arc::new(AppState {
3480            languages: project.read(cx).languages().clone(),
3481            workspace_store,
3482            client,
3483            user_store,
3484            fs: project.read(cx).fs().clone(),
3485            build_window_options: |_, _, _| Default::default(),
3486            initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
3487            node_runtime: FakeNodeRuntime::new(),
3488        });
3489        Self::new(0, project, app_state, cx)
3490    }
3491
3492    //     fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
3493    //         let dock = match position {
3494    //             DockPosition::Left => &self.left_dock,
3495    //             DockPosition::Right => &self.right_dock,
3496    //             DockPosition::Bottom => &self.bottom_dock,
3497    //         };
3498    //         let active_panel = dock.read(cx).visible_panel()?;
3499    //         let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) {
3500    //             dock.read(cx).render_placeholder(cx)
3501    //         } else {
3502    //             ChildView::new(dock, cx).into_any()
3503    //         };
3504
3505    //         Some(
3506    //             element
3507    //                 .constrained()
3508    //                 .dynamically(move |constraint, _, cx| match position {
3509    //                     DockPosition::Left | DockPosition::Right => SizeConstraint::new(
3510    //                         Vector2F::new(20., constraint.min.y()),
3511    //                         Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()),
3512    //                     ),
3513    //                     DockPosition::Bottom => SizeConstraint::new(
3514    //                         Vector2F::new(constraint.min.x(), 20.),
3515    //                         Vector2F::new(constraint.max.x(), cx.window_size().y() * 0.8),
3516    //                     ),
3517    //                 })
3518    //                 .into_any(),
3519    //         )
3520    //     }
3521    // }
3522    pub fn register_action<A: Action>(
3523        &mut self,
3524        callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static,
3525    ) {
3526        let callback = Arc::new(callback);
3527
3528        self.workspace_actions.push(Box::new(move |div| {
3529            let callback = callback.clone();
3530            div.on_action(move |workspace, event, cx| (callback.clone())(workspace, event, cx))
3531        }));
3532    }
3533
3534    fn add_workspace_actions_listeners(
3535        &self,
3536        mut div: Div<Workspace, StatelessInteractivity<Workspace>>,
3537    ) -> Div<Workspace, StatelessInteractivity<Workspace>> {
3538        for action in self.workspace_actions.iter() {
3539            div = (action)(div)
3540        }
3541        div
3542    }
3543
3544    pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
3545    where
3546        B: FnOnce(&mut ViewContext<V>) -> V,
3547    {
3548        self.modal_layer
3549            .update(cx, |modal_layer, cx| modal_layer.toggle_modal(cx, build))
3550    }
3551}
3552
3553fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
3554    let display_origin = cx
3555        .update(|cx| Some(cx.displays().first()?.bounds().origin))
3556        .ok()??;
3557    ZED_WINDOW_POSITION
3558        .zip(*ZED_WINDOW_SIZE)
3559        .map(|(position, size)| {
3560            WindowBounds::Fixed(Bounds {
3561                origin: display_origin + position,
3562                size,
3563            })
3564        })
3565}
3566
3567fn open_items(
3568    serialized_workspace: Option<SerializedWorkspace>,
3569    mut project_paths_to_open: Vec<(PathBuf, Option<ProjectPath>)>,
3570    app_state: Arc<AppState>,
3571    cx: &mut ViewContext<Workspace>,
3572) -> impl 'static + Future<Output = Result<Vec<Option<Result<Box<dyn ItemHandle>>>>>> {
3573    let restored_items = serialized_workspace.map(|serialized_workspace| {
3574        Workspace::load_workspace(
3575            serialized_workspace,
3576            project_paths_to_open
3577                .iter()
3578                .map(|(_, project_path)| project_path)
3579                .cloned()
3580                .collect(),
3581            cx,
3582        )
3583    });
3584
3585    cx.spawn(|workspace, mut cx| async move {
3586        let mut opened_items = Vec::with_capacity(project_paths_to_open.len());
3587
3588        if let Some(restored_items) = restored_items {
3589            let restored_items = restored_items.await?;
3590
3591            let restored_project_paths = restored_items
3592                .iter()
3593                .filter_map(|item| {
3594                    cx.update(|_, cx| item.as_ref()?.project_path(cx))
3595                        .ok()
3596                        .flatten()
3597                })
3598                .collect::<HashSet<_>>();
3599
3600            for restored_item in restored_items {
3601                opened_items.push(restored_item.map(Ok));
3602            }
3603
3604            project_paths_to_open
3605                .iter_mut()
3606                .for_each(|(_, project_path)| {
3607                    if let Some(project_path_to_open) = project_path {
3608                        if restored_project_paths.contains(project_path_to_open) {
3609                            *project_path = None;
3610                        }
3611                    }
3612                });
3613        } else {
3614            for _ in 0..project_paths_to_open.len() {
3615                opened_items.push(None);
3616            }
3617        }
3618        assert!(opened_items.len() == project_paths_to_open.len());
3619
3620        let tasks =
3621            project_paths_to_open
3622                .into_iter()
3623                .enumerate()
3624                .map(|(i, (abs_path, project_path))| {
3625                    let workspace = workspace.clone();
3626                    cx.spawn(|mut cx| {
3627                        let fs = app_state.fs.clone();
3628                        async move {
3629                            let file_project_path = project_path?;
3630                            if fs.is_file(&abs_path).await {
3631                                Some((
3632                                    i,
3633                                    workspace
3634                                        .update(&mut cx, |workspace, cx| {
3635                                            workspace.open_path(file_project_path, None, true, cx)
3636                                        })
3637                                        .log_err()?
3638                                        .await,
3639                                ))
3640                            } else {
3641                                None
3642                            }
3643                        }
3644                    })
3645                });
3646
3647        let tasks = tasks.collect::<Vec<_>>();
3648
3649        let tasks = futures::future::join_all(tasks.into_iter());
3650        for maybe_opened_path in tasks.await.into_iter() {
3651            if let Some((i, path_open_result)) = maybe_opened_path {
3652                opened_items[i] = Some(path_open_result);
3653            }
3654        }
3655
3656        Ok(opened_items)
3657    })
3658}
3659
3660// todo!()
3661// fn notify_of_new_dock(workspace: &WeakView<Workspace>, cx: &mut AsyncAppContext) {
3662//     const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
3663//     const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
3664//     const MESSAGE_ID: usize = 2;
3665
3666//     if workspace
3667//         .read_with(cx, |workspace, cx| {
3668//             workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
3669//         })
3670//         .unwrap_or(false)
3671//     {
3672//         return;
3673//     }
3674
3675//     if db::kvp::KEY_VALUE_STORE
3676//         .read_kvp(NEW_DOCK_HINT_KEY)
3677//         .ok()
3678//         .flatten()
3679//         .is_some()
3680//     {
3681//         if !workspace
3682//             .read_with(cx, |workspace, cx| {
3683//                 workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
3684//             })
3685//             .unwrap_or(false)
3686//         {
3687//             cx.update(|cx| {
3688//                 cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
3689//                     let entry = tracker
3690//                         .entry(TypeId::of::<MessageNotification>())
3691//                         .or_default();
3692//                     if !entry.contains(&MESSAGE_ID) {
3693//                         entry.push(MESSAGE_ID);
3694//                     }
3695//                 });
3696//             });
3697//         }
3698
3699//         return;
3700//     }
3701
3702//     cx.spawn(|_| async move {
3703//         db::kvp::KEY_VALUE_STORE
3704//             .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
3705//             .await
3706//             .ok();
3707//     })
3708//     .detach();
3709
3710//     workspace
3711//         .update(cx, |workspace, cx| {
3712//             workspace.show_notification_once(2, cx, |cx| {
3713//                 cx.build_view(|_| {
3714//                     MessageNotification::new_element(|text, _| {
3715//                         Text::new(
3716//                             "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
3717//                             text,
3718//                         )
3719//                         .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
3720//                             let code_span_background_color = settings::get::<ThemeSettings>(cx)
3721//                                 .theme
3722//                                 .editor
3723//                                 .document_highlight_read_background;
3724
3725//                             cx.scene().push_quad(gpui::Quad {
3726//                                 bounds,
3727//                                 background: Some(code_span_background_color),
3728//                                 border: Default::default(),
3729//                                 corner_radii: (2.0).into(),
3730//                             })
3731//                         })
3732//                         .into_any()
3733//                     })
3734//                     .with_click_message("Read more about the new panel system")
3735//                     .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
3736//                 })
3737//             })
3738//         })
3739//         .ok();
3740
3741fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
3742    const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
3743
3744    workspace
3745        .update(cx, |workspace, cx| {
3746            if (*db2::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
3747                workspace.show_notification_once(0, cx, |cx| {
3748                    cx.build_view(|_| {
3749                        MessageNotification::new("Failed to load the database file.")
3750                            .with_click_message("Click to let us know about this error")
3751                            .on_click(|cx| cx.open_url(REPORT_ISSUE_URL))
3752                    })
3753                });
3754            }
3755        })
3756        .log_err();
3757}
3758
3759impl EventEmitter<Event> for Workspace {}
3760
3761impl Render for Workspace {
3762    type Element = Div<Self>;
3763
3764    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
3765        let mut context = KeyContext::default();
3766        context.add("Workspace");
3767
3768        self.add_workspace_actions_listeners(div())
3769            .context(context)
3770            .relative()
3771            .size_full()
3772            .flex()
3773            .flex_col()
3774            .font("Zed Sans")
3775            .gap_0()
3776            .justify_start()
3777            .items_start()
3778            .text_color(cx.theme().colors().text)
3779            .bg(cx.theme().colors().background)
3780            .child(self.render_titlebar(cx))
3781            .child(
3782                // todo! should this be a component a view?
3783                div()
3784                    .id("workspace")
3785                    .relative()
3786                    .flex_1()
3787                    .w_full()
3788                    .flex()
3789                    .overflow_hidden()
3790                    .border_t()
3791                    .border_b()
3792                    .border_color(cx.theme().colors().border)
3793                    .child(self.modal_layer.clone())
3794                    // .children(
3795                    //     Some(
3796                    //         Panel::new("project-panel-outer", cx)
3797                    //             .side(PanelSide::Left)
3798                    //             .child(ProjectPanel::new("project-panel-inner")),
3799                    //     )
3800                    //     .filter(|_| self.is_project_panel_open()),
3801                    // )
3802                    // .children(
3803                    //     Some(
3804                    //         Panel::new("collab-panel-outer", cx)
3805                    //             .child(CollabPanel::new("collab-panel-inner"))
3806                    //             .side(PanelSide::Left),
3807                    //     )
3808                    //     .filter(|_| self.is_collab_panel_open()),
3809                    // )
3810                    // .child(NotificationToast::new(
3811                    //     "maxbrunsfeld has requested to add you as a contact.".into(),
3812                    // ))
3813                    .child(
3814                        div().flex().flex_col().flex_1().h_full().child(
3815                            div().flex().flex_1().child(self.center.render(
3816                                &self.project,
3817                                &self.follower_states,
3818                                self.active_call(),
3819                                &self.active_pane,
3820                                self.zoomed.as_ref(),
3821                                &self.app_state,
3822                                cx,
3823                            )),
3824                        ), // .children(
3825                           //     Some(
3826                           //         Panel::new("terminal-panel", cx)
3827                           //             .child(Terminal::new())
3828                           //             .allowed_sides(PanelAllowedSides::BottomOnly)
3829                           //             .side(PanelSide::Bottom),
3830                           //     )
3831                           //     .filter(|_| self.is_terminal_open()),
3832                           // ),
3833                    ), // .children(
3834                       //     Some(
3835                       //         Panel::new("chat-panel-outer", cx)
3836                       //             .side(PanelSide::Right)
3837                       //             .child(ChatPanel::new("chat-panel-inner").messages(vec![
3838                       //                 ChatMessage::new(
3839                       //                     "osiewicz".to_string(),
3840                       //                     "is this thing on?".to_string(),
3841                       //                     DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
3842                       //                         .unwrap()
3843                       //                         .naive_local(),
3844                       //                 ),
3845                       //                 ChatMessage::new(
3846                       //                     "maxdeviant".to_string(),
3847                       //                     "Reading you loud and clear!".to_string(),
3848                       //                     DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
3849                       //                         .unwrap()
3850                       //                         .naive_local(),
3851                       //                 ),
3852                       //             ])),
3853                       //     )
3854                       //     .filter(|_| self.is_chat_panel_open()),
3855                       // )
3856                       // .children(
3857                       //     Some(
3858                       //         Panel::new("notifications-panel-outer", cx)
3859                       //             .side(PanelSide::Right)
3860                       //             .child(NotificationsPanel::new("notifications-panel-inner")),
3861                       //     )
3862                       //     .filter(|_| self.is_notifications_panel_open()),
3863                       // )
3864                       // .children(
3865                       //     Some(
3866                       //         Panel::new("assistant-panel-outer", cx)
3867                       //             .child(AssistantPanel::new("assistant-panel-inner")),
3868                       //     )
3869                       //     .filter(|_| self.is_assistant_panel_open()),
3870                       // ),
3871            )
3872            .child(self.status_bar.clone())
3873            // .when(self.debug.show_toast, |this| {
3874            //     this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
3875            // })
3876            // .children(
3877            //     Some(
3878            //         div()
3879            //             .absolute()
3880            //             .top(px(50.))
3881            //             .left(px(640.))
3882            //             .z_index(8)
3883            //             .child(LanguageSelector::new("language-selector")),
3884            //     )
3885            //     .filter(|_| self.is_language_selector_open()),
3886            // )
3887            .z_index(8)
3888            // Debug
3889            .child(
3890                div()
3891                    .flex()
3892                    .flex_col()
3893                    .z_index(9)
3894                    .absolute()
3895                    .top_20()
3896                    .left_1_4()
3897                    .w_40()
3898                    .gap_2(), // .when(self.show_debug, |this| {
3899                              //     this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
3900                              //         Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
3901                              //     ))
3902                              //     .child(
3903                              //         Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
3904                              //             |workspace, cx| workspace.debug_toggle_toast(cx),
3905                              //         )),
3906                              //     )
3907                              //     .child(
3908                              //         Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
3909                              //             |workspace, cx| workspace.debug_toggle_livestream(cx),
3910                              //         )),
3911                              //     )
3912                              // })
3913                              // .child(
3914                              //     Button::<Workspace>::new("Toggle Debug")
3915                              //         .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
3916                              // ),
3917            )
3918    }
3919}
3920// todo!()
3921// impl Entity for Workspace {
3922//     type Event = Event;
3923
3924//     fn release(&mut self, cx: &mut AppContext) {
3925//         self.app_state.workspace_store.update(cx, |store, _| {
3926//             store.workspaces.remove(&self.weak_self);
3927//         })
3928//     }
3929// }
3930
3931// impl View for Workspace {
3932//     fn ui_name() -> &'static str {
3933//         "Workspace"
3934//     }
3935
3936//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
3937//         let theme = theme::current(cx).clone();
3938//         Stack::new()
3939//             .with_child(
3940//                 Flex::column()
3941//                     .with_child(self.render_titlebar(&theme, cx))
3942//                     .with_child(
3943//                         Stack::new()
3944//                             .with_child({
3945//                                 let project = self.project.clone();
3946//                                 Flex::row()
3947//                                     .with_children(self.render_dock(DockPosition::Left, cx))
3948//                                     .with_child(
3949//                                         Flex::column()
3950//                                             .with_child(
3951//                                                 FlexItem::new(
3952//                                                     self.center.render(
3953//                                                         &project,
3954//                                                         &theme,
3955//                                                         &self.follower_states,
3956//                                                         self.active_call(),
3957//                                                         self.active_pane(),
3958//                                                         self.zoomed
3959//                                                             .as_ref()
3960//                                                             .and_then(|zoomed| zoomed.upgrade(cx))
3961//                                                             .as_ref(),
3962//                                                         &self.app_state,
3963//                                                         cx,
3964//                                                     ),
3965//                                                 )
3966//                                                 .flex(1., true),
3967//                                             )
3968//                                             .with_children(
3969//                                                 self.render_dock(DockPosition::Bottom, cx),
3970//                                             )
3971//                                             .flex(1., true),
3972//                                     )
3973//                                     .with_children(self.render_dock(DockPosition::Right, cx))
3974//                             })
3975//                             .with_child(Overlay::new(
3976//                                 Stack::new()
3977//                                     .with_children(self.zoomed.as_ref().and_then(|zoomed| {
3978//                                         enum ZoomBackground {}
3979//                                         let zoomed = zoomed.upgrade(cx)?;
3980
3981//                                         let mut foreground_style =
3982//                                             theme.workspace.zoomed_pane_foreground;
3983//                                         if let Some(zoomed_dock_position) = self.zoomed_position {
3984//                                             foreground_style =
3985//                                                 theme.workspace.zoomed_panel_foreground;
3986//                                             let margin = foreground_style.margin.top;
3987//                                             let border = foreground_style.border.top;
3988
3989//                                             // Only include a margin and border on the opposite side.
3990//                                             foreground_style.margin.top = 0.;
3991//                                             foreground_style.margin.left = 0.;
3992//                                             foreground_style.margin.bottom = 0.;
3993//                                             foreground_style.margin.right = 0.;
3994//                                             foreground_style.border.top = false;
3995//                                             foreground_style.border.left = false;
3996//                                             foreground_style.border.bottom = false;
3997//                                             foreground_style.border.right = false;
3998//                                             match zoomed_dock_position {
3999//                                                 DockPosition::Left => {
4000//                                                     foreground_style.margin.right = margin;
4001//                                                     foreground_style.border.right = border;
4002//                                                 }
4003//                                                 DockPosition::Right => {
4004//                                                     foreground_style.margin.left = margin;
4005//                                                     foreground_style.border.left = border;
4006//                                                 }
4007//                                                 DockPosition::Bottom => {
4008//                                                     foreground_style.margin.top = margin;
4009//                                                     foreground_style.border.top = border;
4010//                                                 }
4011//                                             }
4012//                                         }
4013
4014//                                         Some(
4015//                                             ChildView::new(&zoomed, cx)
4016//                                                 .contained()
4017//                                                 .with_style(foreground_style)
4018//                                                 .aligned()
4019//                                                 .contained()
4020//                                                 .with_style(theme.workspace.zoomed_background)
4021//                                                 .mouse::<ZoomBackground>(0)
4022//                                                 .capture_all()
4023//                                                 .on_down(
4024//                                                     MouseButton::Left,
4025//                                                     |_, this: &mut Self, cx| {
4026//                                                         this.zoom_out(cx);
4027//                                                     },
4028//                                                 ),
4029//                                         )
4030//                                     }))
4031//                                     .with_children(self.modal.as_ref().map(|modal| {
4032//                                         // Prevent clicks within the modal from falling
4033//                                         // through to the rest of the workspace.
4034//                                         enum ModalBackground {}
4035//                                         MouseEventHandler::new::<ModalBackground, _>(
4036//                                             0,
4037//                                             cx,
4038//                                             |_, cx| ChildView::new(modal.view.as_any(), cx),
4039//                                         )
4040//                                         .on_click(MouseButton::Left, |_, _, _| {})
4041//                                         .contained()
4042//                                         .with_style(theme.workspace.modal)
4043//                                         .aligned()
4044//                                         .top()
4045//                                     }))
4046//                                     .with_children(self.render_notifications(&theme.workspace, cx)),
4047//                             ))
4048//                             .provide_resize_bounds::<WorkspaceBounds>()
4049//                             .flex(1.0, true),
4050//                     )
4051//                     .with_child(ChildView::new(&self.status_bar, cx))
4052//                     .contained()
4053//                     .with_background_color(theme.workspace.background),
4054//             )
4055//             .with_children(DragAndDrop::render(cx))
4056//             .with_children(self.render_disconnected_overlay(cx))
4057//             .into_any_named("workspace")
4058//     }
4059
4060//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
4061//         if cx.is_self_focused() {
4062//             cx.focus(&self.active_pane);
4063//         }
4064//     }
4065
4066//     fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
4067//         DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
4068//     }
4069// }
4070
4071impl WorkspaceStore {
4072    pub fn new(client: Arc<Client>, _cx: &mut ModelContext<Self>) -> Self {
4073        Self {
4074            workspaces: Default::default(),
4075            followers: Default::default(),
4076            _subscriptions: vec![],
4077            //     client.add_request_handler(cx.weak_model(), Self::handle_follow),
4078            //     client.add_message_handler(cx.weak_model(), Self::handle_unfollow),
4079            //     client.add_message_handler(cx.weak_model(), Self::handle_update_followers),
4080            // ],
4081            client,
4082        }
4083    }
4084
4085    pub fn update_followers(
4086        &self,
4087        project_id: Option<u64>,
4088        update: proto::update_followers::Variant,
4089        cx: &AppContext,
4090    ) -> Option<()> {
4091        if !cx.has_global::<Model<ActiveCall>>() {
4092            return None;
4093        }
4094
4095        let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id();
4096        let follower_ids: Vec<_> = self
4097            .followers
4098            .iter()
4099            .filter_map(|follower| {
4100                if follower.project_id == project_id || project_id.is_none() {
4101                    Some(follower.peer_id.into())
4102                } else {
4103                    None
4104                }
4105            })
4106            .collect();
4107        if follower_ids.is_empty() {
4108            return None;
4109        }
4110        self.client
4111            .send(proto::UpdateFollowers {
4112                room_id,
4113                project_id,
4114                follower_ids,
4115                variant: Some(update),
4116            })
4117            .log_err()
4118    }
4119
4120    pub async fn handle_follow(
4121        this: Model<Self>,
4122        envelope: TypedEnvelope<proto::Follow>,
4123        _: Arc<Client>,
4124        mut cx: AsyncAppContext,
4125    ) -> Result<proto::FollowResponse> {
4126        this.update(&mut cx, |this, cx| {
4127            let follower = Follower {
4128                project_id: envelope.payload.project_id,
4129                peer_id: envelope.original_sender_id()?,
4130            };
4131            let active_project = ActiveCall::global(cx).read(cx).location().cloned();
4132
4133            let mut response = proto::FollowResponse::default();
4134            for workspace in &this.workspaces {
4135                workspace
4136                    .update(cx, |workspace, cx| {
4137                        let handler_response = workspace.handle_follow(follower.project_id, cx);
4138                        if response.views.is_empty() {
4139                            response.views = handler_response.views;
4140                        } else {
4141                            response.views.extend_from_slice(&handler_response.views);
4142                        }
4143
4144                        if let Some(active_view_id) = handler_response.active_view_id.clone() {
4145                            if response.active_view_id.is_none()
4146                                || Some(workspace.project.downgrade()) == active_project
4147                            {
4148                                response.active_view_id = Some(active_view_id);
4149                            }
4150                        }
4151                    })
4152                    .ok();
4153            }
4154
4155            if let Err(ix) = this.followers.binary_search(&follower) {
4156                this.followers.insert(ix, follower);
4157            }
4158
4159            Ok(response)
4160        })?
4161    }
4162
4163    async fn handle_unfollow(
4164        model: Model<Self>,
4165        envelope: TypedEnvelope<proto::Unfollow>,
4166        _: Arc<Client>,
4167        mut cx: AsyncAppContext,
4168    ) -> Result<()> {
4169        model.update(&mut cx, |this, _| {
4170            let follower = Follower {
4171                project_id: envelope.payload.project_id,
4172                peer_id: envelope.original_sender_id()?,
4173            };
4174            if let Ok(ix) = this.followers.binary_search(&follower) {
4175                this.followers.remove(ix);
4176            }
4177            Ok(())
4178        })?
4179    }
4180
4181    async fn handle_update_followers(
4182        this: Model<Self>,
4183        envelope: TypedEnvelope<proto::UpdateFollowers>,
4184        _: Arc<Client>,
4185        mut cx: AsyncWindowContext,
4186    ) -> Result<()> {
4187        let leader_id = envelope.original_sender_id()?;
4188        let update = envelope.payload;
4189
4190        this.update(&mut cx, |this, cx| {
4191            for workspace in &this.workspaces {
4192                workspace.update(cx, |workspace, cx| {
4193                    let project_id = workspace.project.read(cx).remote_id();
4194                    if update.project_id != project_id && update.project_id.is_some() {
4195                        return;
4196                    }
4197                    workspace.handle_update_followers(leader_id, update.clone(), cx);
4198                })?;
4199            }
4200            Ok(())
4201        })?
4202    }
4203}
4204
4205impl ViewId {
4206    pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
4207        Ok(Self {
4208            creator: message
4209                .creator
4210                .ok_or_else(|| anyhow!("creator is missing"))?,
4211            id: message.id,
4212        })
4213    }
4214
4215    pub(crate) fn to_proto(&self) -> proto::ViewId {
4216        proto::ViewId {
4217            creator: Some(self.creator),
4218            id: self.id,
4219        }
4220    }
4221}
4222
4223pub trait WorkspaceHandle {
4224    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
4225}
4226
4227impl WorkspaceHandle for View<Workspace> {
4228    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
4229        self.read(cx)
4230            .worktrees(cx)
4231            .flat_map(|worktree| {
4232                let worktree_id = worktree.read(cx).id();
4233                worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
4234                    worktree_id,
4235                    path: f.path.clone(),
4236                })
4237            })
4238            .collect::<Vec<_>>()
4239    }
4240}
4241
4242// impl std::fmt::Debug for OpenPaths {
4243//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4244//         f.debug_struct("OpenPaths")
4245//             .field("paths", &self.paths)
4246//             .finish()
4247//     }
4248// }
4249
4250pub struct WorkspaceCreated(pub WeakView<Workspace>);
4251
4252pub fn activate_workspace_for_project(
4253    cx: &mut AppContext,
4254    predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static,
4255) -> Option<WindowHandle<Workspace>> {
4256    for window in cx.windows() {
4257        let Some(workspace) = window.downcast::<Workspace>() else {
4258            continue;
4259        };
4260
4261        let predicate = workspace
4262            .update(cx, |workspace, cx| {
4263                let project = workspace.project.read(cx);
4264                if predicate(project, cx) {
4265                    cx.activate_window();
4266                    true
4267                } else {
4268                    false
4269                }
4270            })
4271            .log_err()
4272            .unwrap_or(false);
4273
4274        if predicate {
4275            return Some(workspace);
4276        }
4277    }
4278
4279    None
4280}
4281
4282pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
4283    DB.last_workspace().await.log_err().flatten()
4284}
4285
4286// async fn join_channel_internal(
4287//     channel_id: u64,
4288//     app_state: &Arc<AppState>,
4289//     requesting_window: Option<WindowHandle<Workspace>>,
4290//     active_call: &ModelHandle<ActiveCall>,
4291//     cx: &mut AsyncAppContext,
4292// ) -> Result<bool> {
4293//     let (should_prompt, open_room) = active_call.read_with(cx, |active_call, cx| {
4294//         let Some(room) = active_call.room().map(|room| room.read(cx)) else {
4295//             return (false, None);
4296//         };
4297
4298//         let already_in_channel = room.channel_id() == Some(channel_id);
4299//         let should_prompt = room.is_sharing_project()
4300//             && room.remote_participants().len() > 0
4301//             && !already_in_channel;
4302//         let open_room = if already_in_channel {
4303//             active_call.room().cloned()
4304//         } else {
4305//             None
4306//         };
4307//         (should_prompt, open_room)
4308//     });
4309
4310//     if let Some(room) = open_room {
4311//         let task = room.update(cx, |room, cx| {
4312//             if let Some((project, host)) = room.most_active_project(cx) {
4313//                 return Some(join_remote_project(project, host, app_state.clone(), cx));
4314//             }
4315
4316//             None
4317//         });
4318//         if let Some(task) = task {
4319//             task.await?;
4320//         }
4321//         return anyhow::Ok(true);
4322//     }
4323
4324//     if should_prompt {
4325//         if let Some(workspace) = requesting_window {
4326//             if let Some(window) = workspace.update(cx, |cx| cx.window()) {
4327//                 let answer = window.prompt(
4328//                     PromptLevel::Warning,
4329//                     "Leaving this call will unshare your current project.\nDo you want to switch channels?",
4330//                     &["Yes, Join Channel", "Cancel"],
4331//                     cx,
4332//                 );
4333
4334//                 if let Some(mut answer) = answer {
4335//                     if answer.next().await == Some(1) {
4336//                         return Ok(false);
4337//                     }
4338//                 }
4339//             } else {
4340//                 return Ok(false); // unreachable!() hopefully
4341//             }
4342//         } else {
4343//             return Ok(false); // unreachable!() hopefully
4344//         }
4345//     }
4346
4347//     let client = cx.read(|cx| active_call.read(cx).client());
4348
4349//     let mut client_status = client.status();
4350
4351//     // this loop will terminate within client::CONNECTION_TIMEOUT seconds.
4352//     'outer: loop {
4353//         let Some(status) = client_status.recv().await else {
4354//             return Err(anyhow!("error connecting"));
4355//         };
4356
4357//         match status {
4358//             Status::Connecting
4359//             | Status::Authenticating
4360//             | Status::Reconnecting
4361//             | Status::Reauthenticating => continue,
4362//             Status::Connected { .. } => break 'outer,
4363//             Status::SignedOut => return Err(anyhow!("not signed in")),
4364//             Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
4365//             Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
4366//                 return Err(anyhow!("zed is offline"))
4367//             }
4368//         }
4369//     }
4370
4371//     let room = active_call
4372//         .update(cx, |active_call, cx| {
4373//             active_call.join_channel(channel_id, cx)
4374//         })
4375//         .await?;
4376
4377//     room.update(cx, |room, _| room.room_update_completed())
4378//         .await;
4379
4380//     let task = room.update(cx, |room, cx| {
4381//         if let Some((project, host)) = room.most_active_project(cx) {
4382//             return Some(join_remote_project(project, host, app_state.clone(), cx));
4383//         }
4384
4385//         None
4386//     });
4387//     if let Some(task) = task {
4388//         task.await?;
4389//         return anyhow::Ok(true);
4390//     }
4391//     anyhow::Ok(false)
4392// }
4393
4394// pub fn join_channel(
4395//     channel_id: u64,
4396//     app_state: Arc<AppState>,
4397//     requesting_window: Option<WindowHandle<Workspace>>,
4398//     cx: &mut AppContext,
4399// ) -> Task<Result<()>> {
4400//     let active_call = ActiveCall::global(cx);
4401//     cx.spawn(|mut cx| async move {
4402//         let result = join_channel_internal(
4403//             channel_id,
4404//             &app_state,
4405//             requesting_window,
4406//             &active_call,
4407//             &mut cx,
4408//         )
4409//         .await;
4410
4411//         // join channel succeeded, and opened a window
4412//         if matches!(result, Ok(true)) {
4413//             return anyhow::Ok(());
4414//         }
4415
4416//         if requesting_window.is_some() {
4417//             return anyhow::Ok(());
4418//         }
4419
4420//         // find an existing workspace to focus and show call controls
4421//         let mut active_window = activate_any_workspace_window(&mut cx);
4422//         if active_window.is_none() {
4423//             // no open workspaces, make one to show the error in (blergh)
4424//             cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
4425//                 .await;
4426//         }
4427
4428//         active_window = activate_any_workspace_window(&mut cx);
4429//         if active_window.is_none() {
4430//             return result.map(|_| ()); // unreachable!() assuming new_local always opens a window
4431//         }
4432
4433//         if let Err(err) = result {
4434//             let prompt = active_window.unwrap().prompt(
4435//                 PromptLevel::Critical,
4436//                 &format!("Failed to join channel: {}", err),
4437//                 &["Ok"],
4438//                 &mut cx,
4439//             );
4440//             if let Some(mut prompt) = prompt {
4441//                 prompt.next().await;
4442//             } else {
4443//                 return Err(err);
4444//             }
4445//         }
4446
4447//         // return ok, we showed the error to the user.
4448//         return anyhow::Ok(());
4449//     })
4450// }
4451
4452// pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
4453//     for window in cx.windows() {
4454//         let found = window.update(cx, |cx| {
4455//             let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
4456//             if is_workspace {
4457//                 cx.activate_window();
4458//             }
4459//             is_workspace
4460//         });
4461//         if found == Some(true) {
4462//             return Some(window);
4463//         }
4464//     }
4465//     None
4466// }
4467
4468#[allow(clippy::type_complexity)]
4469pub fn open_paths(
4470    abs_paths: &[PathBuf],
4471    app_state: &Arc<AppState>,
4472    requesting_window: Option<WindowHandle<Workspace>>,
4473    cx: &mut AppContext,
4474) -> Task<
4475    anyhow::Result<(
4476        WindowHandle<Workspace>,
4477        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
4478    )>,
4479> {
4480    let app_state = app_state.clone();
4481    let abs_paths = abs_paths.to_vec();
4482    // Open paths in existing workspace if possible
4483    let existing = activate_workspace_for_project(cx, {
4484        let abs_paths = abs_paths.clone();
4485        move |project, cx| project.contains_paths(&abs_paths, cx)
4486    });
4487    cx.spawn(move |mut cx| async move {
4488        if let Some(existing) = existing {
4489            // // Ok((
4490            //     existing.clone(),
4491            //     cx.update_window_root(&existing, |workspace, cx| {
4492            //         workspace.open_paths(abs_paths, true, cx)
4493            //     })?
4494            //     .await,
4495            // ))
4496            todo!()
4497        } else {
4498            cx.update(move |cx| {
4499                Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
4500            })?
4501            .await
4502        }
4503    })
4504}
4505
4506pub fn open_new(
4507    app_state: &Arc<AppState>,
4508    cx: &mut AppContext,
4509    init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static + Send,
4510) -> Task<()> {
4511    let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
4512    cx.spawn(|mut cx| async move {
4513        if let Some((workspace, opened_paths)) = task.await.log_err() {
4514            workspace
4515                .update(&mut cx, |workspace, cx| {
4516                    if opened_paths.is_empty() {
4517                        init(workspace, cx)
4518                    }
4519                })
4520                .log_err();
4521        }
4522    })
4523}
4524
4525// pub fn create_and_open_local_file(
4526//     path: &'static Path,
4527//     cx: &mut ViewContext<Workspace>,
4528//     default_content: impl 'static + Send + FnOnce() -> Rope,
4529// ) -> Task<Result<Box<dyn ItemHandle>>> {
4530//     cx.spawn(|workspace, mut cx| async move {
4531//         let fs = workspace.read_with(&cx, |workspace, _| workspace.app_state().fs.clone())?;
4532//         if !fs.is_file(path).await {
4533//             fs.create_file(path, Default::default()).await?;
4534//             fs.save(path, &default_content(), Default::default())
4535//                 .await?;
4536//         }
4537
4538//         let mut items = workspace
4539//             .update(&mut cx, |workspace, cx| {
4540//                 workspace.with_local_workspace(cx, |workspace, cx| {
4541//                     workspace.open_paths(vec![path.to_path_buf()], false, cx)
4542//                 })
4543//             })?
4544//             .await?
4545//             .await;
4546
4547//         let item = items.pop().flatten();
4548//         item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
4549//     })
4550// }
4551
4552// pub fn join_remote_project(
4553//     project_id: u64,
4554//     follow_user_id: u64,
4555//     app_state: Arc<AppState>,
4556//     cx: &mut AppContext,
4557// ) -> Task<Result<()>> {
4558//     cx.spawn(|mut cx| async move {
4559//         let windows = cx.windows();
4560//         let existing_workspace = windows.into_iter().find_map(|window| {
4561//             window.downcast::<Workspace>().and_then(|window| {
4562//                 window
4563//                     .read_root_with(&cx, |workspace, cx| {
4564//                         if workspace.project().read(cx).remote_id() == Some(project_id) {
4565//                             Some(cx.handle().downgrade())
4566//                         } else {
4567//                             None
4568//                         }
4569//                     })
4570//                     .unwrap_or(None)
4571//             })
4572//         });
4573
4574//         let workspace = if let Some(existing_workspace) = existing_workspace {
4575//             existing_workspace
4576//         } else {
4577//             let active_call = cx.read(ActiveCall::global);
4578//             let room = active_call
4579//                 .read_with(&cx, |call, _| call.room().cloned())
4580//                 .ok_or_else(|| anyhow!("not in a call"))?;
4581//             let project = room
4582//                 .update(&mut cx, |room, cx| {
4583//                     room.join_project(
4584//                         project_id,
4585//                         app_state.languages.clone(),
4586//                         app_state.fs.clone(),
4587//                         cx,
4588//                     )
4589//                 })
4590//                 .await?;
4591
4592//             let window_bounds_override = window_bounds_env_override(&cx);
4593//             let window = cx.add_window(
4594//                 (app_state.build_window_options)(
4595//                     window_bounds_override,
4596//                     None,
4597//                     cx.platform().as_ref(),
4598//                 ),
4599//                 |cx| Workspace::new(0, project, app_state.clone(), cx),
4600//             );
4601//             let workspace = window.root(&cx).unwrap();
4602//             (app_state.initialize_workspace)(
4603//                 workspace.downgrade(),
4604//                 false,
4605//                 app_state.clone(),
4606//                 cx.clone(),
4607//             )
4608//             .await
4609//             .log_err();
4610
4611//             workspace.downgrade()
4612//         };
4613
4614//         workspace.window().activate(&mut cx);
4615//         cx.platform().activate(true);
4616
4617//         workspace.update(&mut cx, |workspace, cx| {
4618//             if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
4619//                 let follow_peer_id = room
4620//                     .read(cx)
4621//                     .remote_participants()
4622//                     .iter()
4623//                     .find(|(_, participant)| participant.user.id == follow_user_id)
4624//                     .map(|(_, p)| p.peer_id)
4625//                     .or_else(|| {
4626//                         // If we couldn't follow the given user, follow the host instead.
4627//                         let collaborator = workspace
4628//                             .project()
4629//                             .read(cx)
4630//                             .collaborators()
4631//                             .values()
4632//                             .find(|collaborator| collaborator.replica_id == 0)?;
4633//                         Some(collaborator.peer_id)
4634//                     });
4635
4636//                 if let Some(follow_peer_id) = follow_peer_id {
4637//                     workspace
4638//                         .follow(follow_peer_id, cx)
4639//                         .map(|follow| follow.detach_and_log_err(cx));
4640//                 }
4641//             }
4642//         })?;
4643
4644//         anyhow::Ok(())
4645//     })
4646// }
4647
4648// pub fn restart(_: &Restart, cx: &mut AppContext) {
4649//     let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
4650//     cx.spawn(|mut cx| async move {
4651//         let mut workspace_windows = cx
4652//             .windows()
4653//             .into_iter()
4654//             .filter_map(|window| window.downcast::<Workspace>())
4655//             .collect::<Vec<_>>();
4656
4657//         // If multiple windows have unsaved changes, and need a save prompt,
4658//         // prompt in the active window before switching to a different window.
4659//         workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
4660
4661//         if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
4662//             let answer = window.prompt(
4663//                 PromptLevel::Info,
4664//                 "Are you sure you want to restart?",
4665//                 &["Restart", "Cancel"],
4666//                 &mut cx,
4667//             );
4668
4669//             if let Some(mut answer) = answer {
4670//                 let answer = answer.next().await;
4671//                 if answer != Some(0) {
4672//                     return Ok(());
4673//                 }
4674//             }
4675//         }
4676
4677//         // If the user cancels any save prompt, then keep the app open.
4678//         for window in workspace_windows {
4679//             if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
4680//                 workspace.prepare_to_close(true, cx)
4681//             }) {
4682//                 if !should_close.await? {
4683//                     return Ok(());
4684//                 }
4685//             }
4686//         }
4687//         cx.platform().restart();
4688//         anyhow::Ok(())
4689//     })
4690//     .detach_and_log_err(cx);
4691// }
4692
4693fn parse_pixel_position_env_var(value: &str) -> Option<Point<GlobalPixels>> {
4694    let mut parts = value.split(',');
4695    let x: usize = parts.next()?.parse().ok()?;
4696    let y: usize = parts.next()?.parse().ok()?;
4697    Some(point((x as f64).into(), (y as f64).into()))
4698}
4699
4700fn parse_pixel_size_env_var(value: &str) -> Option<Size<GlobalPixels>> {
4701    let mut parts = value.split(',');
4702    let width: usize = parts.next()?.parse().ok()?;
4703    let height: usize = parts.next()?.parse().ok()?;
4704    Some(size((width as f64).into(), (height as f64).into()))
4705}
4706
4707// #[cfg(test)]
4708// mod tests {
4709//     use super::*;
4710//     use crate::{
4711//         dock::test::{TestPanel, TestPanelEvent},
4712//         item::test::{TestItem, TestItemEvent, TestProjectItem},
4713//     };
4714//     use fs::FakeFs;
4715//     use gpui::{executor::Deterministic, test::EmptyView, TestAppContext};
4716//     use project::{Project, ProjectEntryId};
4717//     use serde_json::json;
4718//     use settings::SettingsStore;
4719//     use std::{cell::RefCell, rc::Rc};
4720
4721//     #[gpui::test]
4722//     async fn test_tab_disambiguation(cx: &mut TestAppContext) {
4723//         init_test(cx);
4724
4725//         let fs = FakeFs::new(cx.background());
4726//         let project = Project::test(fs, [], cx).await;
4727//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4728//         let workspace = window.root(cx);
4729
4730//         // Adding an item with no ambiguity renders the tab without detail.
4731//         let item1 = window.build_view(cx, |_| {
4732//             let mut item = TestItem::new();
4733//             item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
4734//             item
4735//         });
4736//         workspace.update(cx, |workspace, cx| {
4737//             workspace.add_item(Box::new(item1.clone()), cx);
4738//         });
4739//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None));
4740
4741//         // Adding an item that creates ambiguity increases the level of detail on
4742//         // both tabs.
4743//         let item2 = window.build_view(cx, |_| {
4744//             let mut item = TestItem::new();
4745//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4746//             item
4747//         });
4748//         workspace.update(cx, |workspace, cx| {
4749//             workspace.add_item(Box::new(item2.clone()), cx);
4750//         });
4751//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4752//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4753
4754//         // Adding an item that creates ambiguity increases the level of detail only
4755//         // on the ambiguous tabs. In this case, the ambiguity can't be resolved so
4756//         // we stop at the highest detail available.
4757//         let item3 = window.build_view(cx, |_| {
4758//             let mut item = TestItem::new();
4759//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4760//             item
4761//         });
4762//         workspace.update(cx, |workspace, cx| {
4763//             workspace.add_item(Box::new(item3.clone()), cx);
4764//         });
4765//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4766//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4767//         item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4768//     }
4769
4770//     #[gpui::test]
4771//     async fn test_tracking_active_path(cx: &mut TestAppContext) {
4772//         init_test(cx);
4773
4774//         let fs = FakeFs::new(cx.background());
4775//         fs.insert_tree(
4776//             "/root1",
4777//             json!({
4778//                 "one.txt": "",
4779//                 "two.txt": "",
4780//             }),
4781//         )
4782//         .await;
4783//         fs.insert_tree(
4784//             "/root2",
4785//             json!({
4786//                 "three.txt": "",
4787//             }),
4788//         )
4789//         .await;
4790
4791//         let project = Project::test(fs, ["root1".as_ref()], cx).await;
4792//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4793//         let workspace = window.root(cx);
4794//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4795//         let worktree_id = project.read_with(cx, |project, cx| {
4796//             project.worktrees(cx).next().unwrap().read(cx).id()
4797//         });
4798
4799//         let item1 = window.build_view(cx, |cx| {
4800//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
4801//         });
4802//         let item2 = window.build_view(cx, |cx| {
4803//             TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
4804//         });
4805
4806//         // Add an item to an empty pane
4807//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx));
4808//         project.read_with(cx, |project, cx| {
4809//             assert_eq!(
4810//                 project.active_entry(),
4811//                 project
4812//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4813//                     .map(|e| e.id)
4814//             );
4815//         });
4816//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4817
4818//         // Add a second item to a non-empty pane
4819//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
4820//         assert_eq!(window.current_title(cx).as_deref(), Some("two.txt β€” root1"));
4821//         project.read_with(cx, |project, cx| {
4822//             assert_eq!(
4823//                 project.active_entry(),
4824//                 project
4825//                     .entry_for_path(&(worktree_id, "two.txt").into(), cx)
4826//                     .map(|e| e.id)
4827//             );
4828//         });
4829
4830//         // Close the active item
4831//         pane.update(cx, |pane, cx| {
4832//             pane.close_active_item(&Default::default(), cx).unwrap()
4833//         })
4834//         .await
4835//         .unwrap();
4836//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4837//         project.read_with(cx, |project, cx| {
4838//             assert_eq!(
4839//                 project.active_entry(),
4840//                 project
4841//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4842//                     .map(|e| e.id)
4843//             );
4844//         });
4845
4846//         // Add a project folder
4847//         project
4848//             .update(cx, |project, cx| {
4849//                 project.find_or_create_local_worktree("/root2", true, cx)
4850//             })
4851//             .await
4852//             .unwrap();
4853//         assert_eq!(
4854//             window.current_title(cx).as_deref(),
4855//             Some("one.txt β€” root1, root2")
4856//         );
4857
4858//         // Remove a project folder
4859//         project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
4860//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root2"));
4861//     }
4862
4863//     #[gpui::test]
4864//     async fn test_close_window(cx: &mut TestAppContext) {
4865//         init_test(cx);
4866
4867//         let fs = FakeFs::new(cx.background());
4868//         fs.insert_tree("/root", json!({ "one": "" })).await;
4869
4870//         let project = Project::test(fs, ["root".as_ref()], cx).await;
4871//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4872//         let workspace = window.root(cx);
4873
4874//         // When there are no dirty items, there's nothing to do.
4875//         let item1 = window.build_view(cx, |_| TestItem::new());
4876//         workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
4877//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4878//         assert!(task.await.unwrap());
4879
4880//         // When there are dirty untitled items, prompt to save each one. If the user
4881//         // cancels any prompt, then abort.
4882//         let item2 = window.build_view(cx, |_| TestItem::new().with_dirty(true));
4883//         let item3 = window.build_view(cx, |cx| {
4884//             TestItem::new()
4885//                 .with_dirty(true)
4886//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4887//         });
4888//         workspace.update(cx, |w, cx| {
4889//             w.add_item(Box::new(item2.clone()), cx);
4890//             w.add_item(Box::new(item3.clone()), cx);
4891//         });
4892//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4893//         cx.foreground().run_until_parked();
4894//         window.simulate_prompt_answer(2, cx); // cancel save all
4895//         cx.foreground().run_until_parked();
4896//         window.simulate_prompt_answer(2, cx); // cancel save all
4897//         cx.foreground().run_until_parked();
4898//         assert!(!window.has_pending_prompt(cx));
4899//         assert!(!task.await.unwrap());
4900//     }
4901
4902//     #[gpui::test]
4903//     async fn test_close_pane_items(cx: &mut TestAppContext) {
4904//         init_test(cx);
4905
4906//         let fs = FakeFs::new(cx.background());
4907
4908//         let project = Project::test(fs, None, cx).await;
4909//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4910//         let workspace = window.root(cx);
4911
4912//         let item1 = window.build_view(cx, |cx| {
4913//             TestItem::new()
4914//                 .with_dirty(true)
4915//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4916//         });
4917//         let item2 = window.build_view(cx, |cx| {
4918//             TestItem::new()
4919//                 .with_dirty(true)
4920//                 .with_conflict(true)
4921//                 .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
4922//         });
4923//         let item3 = window.build_view(cx, |cx| {
4924//             TestItem::new()
4925//                 .with_dirty(true)
4926//                 .with_conflict(true)
4927//                 .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
4928//         });
4929//         let item4 = window.build_view(cx, |cx| {
4930//             TestItem::new()
4931//                 .with_dirty(true)
4932//                 .with_project_items(&[TestProjectItem::new_untitled(cx)])
4933//         });
4934//         let pane = workspace.update(cx, |workspace, cx| {
4935//             workspace.add_item(Box::new(item1.clone()), cx);
4936//             workspace.add_item(Box::new(item2.clone()), cx);
4937//             workspace.add_item(Box::new(item3.clone()), cx);
4938//             workspace.add_item(Box::new(item4.clone()), cx);
4939//             workspace.active_pane().clone()
4940//         });
4941
4942//         let close_items = pane.update(cx, |pane, cx| {
4943//             pane.activate_item(1, true, true, cx);
4944//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4945//             let item1_id = item1.id();
4946//             let item3_id = item3.id();
4947//             let item4_id = item4.id();
4948//             pane.close_items(cx, SaveIntent::Close, move |id| {
4949//                 [item1_id, item3_id, item4_id].contains(&id)
4950//             })
4951//         });
4952//         cx.foreground().run_until_parked();
4953
4954//         assert!(window.has_pending_prompt(cx));
4955//         // Ignore "Save all" prompt
4956//         window.simulate_prompt_answer(2, cx);
4957//         cx.foreground().run_until_parked();
4958//         // There's a prompt to save item 1.
4959//         pane.read_with(cx, |pane, _| {
4960//             assert_eq!(pane.items_len(), 4);
4961//             assert_eq!(pane.active_item().unwrap().id(), item1.id());
4962//         });
4963//         // Confirm saving item 1.
4964//         window.simulate_prompt_answer(0, cx);
4965//         cx.foreground().run_until_parked();
4966
4967//         // Item 1 is saved. There's a prompt to save item 3.
4968//         pane.read_with(cx, |pane, cx| {
4969//             assert_eq!(item1.read(cx).save_count, 1);
4970//             assert_eq!(item1.read(cx).save_as_count, 0);
4971//             assert_eq!(item1.read(cx).reload_count, 0);
4972//             assert_eq!(pane.items_len(), 3);
4973//             assert_eq!(pane.active_item().unwrap().id(), item3.id());
4974//         });
4975//         assert!(window.has_pending_prompt(cx));
4976
4977//         // Cancel saving item 3.
4978//         window.simulate_prompt_answer(1, cx);
4979//         cx.foreground().run_until_parked();
4980
4981//         // Item 3 is reloaded. There's a prompt to save item 4.
4982//         pane.read_with(cx, |pane, cx| {
4983//             assert_eq!(item3.read(cx).save_count, 0);
4984//             assert_eq!(item3.read(cx).save_as_count, 0);
4985//             assert_eq!(item3.read(cx).reload_count, 1);
4986//             assert_eq!(pane.items_len(), 2);
4987//             assert_eq!(pane.active_item().unwrap().id(), item4.id());
4988//         });
4989//         assert!(window.has_pending_prompt(cx));
4990
4991//         // Confirm saving item 4.
4992//         window.simulate_prompt_answer(0, cx);
4993//         cx.foreground().run_until_parked();
4994
4995//         // There's a prompt for a path for item 4.
4996//         cx.simulate_new_path_selection(|_| Some(Default::default()));
4997//         close_items.await.unwrap();
4998
4999//         // The requested items are closed.
5000//         pane.read_with(cx, |pane, cx| {
5001//             assert_eq!(item4.read(cx).save_count, 0);
5002//             assert_eq!(item4.read(cx).save_as_count, 1);
5003//             assert_eq!(item4.read(cx).reload_count, 0);
5004//             assert_eq!(pane.items_len(), 1);
5005//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
5006//         });
5007//     }
5008
5009//     #[gpui::test]
5010//     async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) {
5011//         init_test(cx);
5012
5013//         let fs = FakeFs::new(cx.background());
5014
5015//         let project = Project::test(fs, [], cx).await;
5016//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5017//         let workspace = window.root(cx);
5018
5019//         // Create several workspace items with single project entries, and two
5020//         // workspace items with multiple project entries.
5021//         let single_entry_items = (0..=4)
5022//             .map(|project_entry_id| {
5023//                 window.build_view(cx, |cx| {
5024//                     TestItem::new()
5025//                         .with_dirty(true)
5026//                         .with_project_items(&[TestProjectItem::new(
5027//                             project_entry_id,
5028//                             &format!("{project_entry_id}.txt"),
5029//                             cx,
5030//                         )])
5031//                 })
5032//             })
5033//             .collect::<Vec<_>>();
5034//         let item_2_3 = window.build_view(cx, |cx| {
5035//             TestItem::new()
5036//                 .with_dirty(true)
5037//                 .with_singleton(false)
5038//                 .with_project_items(&[
5039//                     single_entry_items[2].read(cx).project_items[0].clone(),
5040//                     single_entry_items[3].read(cx).project_items[0].clone(),
5041//                 ])
5042//         });
5043//         let item_3_4 = window.build_view(cx, |cx| {
5044//             TestItem::new()
5045//                 .with_dirty(true)
5046//                 .with_singleton(false)
5047//                 .with_project_items(&[
5048//                     single_entry_items[3].read(cx).project_items[0].clone(),
5049//                     single_entry_items[4].read(cx).project_items[0].clone(),
5050//                 ])
5051//         });
5052
5053//         // Create two panes that contain the following project entries:
5054//         //   left pane:
5055//         //     multi-entry items:   (2, 3)
5056//         //     single-entry items:  0, 1, 2, 3, 4
5057//         //   right pane:
5058//         //     single-entry items:  1
5059//         //     multi-entry items:   (3, 4)
5060//         let left_pane = workspace.update(cx, |workspace, cx| {
5061//             let left_pane = workspace.active_pane().clone();
5062//             workspace.add_item(Box::new(item_2_3.clone()), cx);
5063//             for item in single_entry_items {
5064//                 workspace.add_item(Box::new(item), cx);
5065//             }
5066//             left_pane.update(cx, |pane, cx| {
5067//                 pane.activate_item(2, true, true, cx);
5068//             });
5069
5070//             workspace
5071//                 .split_and_clone(left_pane.clone(), SplitDirection::Right, cx)
5072//                 .unwrap();
5073
5074//             left_pane
5075//         });
5076
5077//         //Need to cause an effect flush in order to respect new focus
5078//         workspace.update(cx, |workspace, cx| {
5079//             workspace.add_item(Box::new(item_3_4.clone()), cx);
5080//             cx.focus(&left_pane);
5081//         });
5082
5083//         // When closing all of the items in the left pane, we should be prompted twice:
5084//         // once for project entry 0, and once for project entry 2. After those two
5085//         // prompts, the task should complete.
5086
5087//         let close = left_pane.update(cx, |pane, cx| {
5088//             pane.close_items(cx, SaveIntent::Close, move |_| true)
5089//         });
5090//         cx.foreground().run_until_parked();
5091//         // Discard "Save all" prompt
5092//         window.simulate_prompt_answer(2, cx);
5093
5094//         cx.foreground().run_until_parked();
5095//         left_pane.read_with(cx, |pane, cx| {
5096//             assert_eq!(
5097//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
5098//                 &[ProjectEntryId::from_proto(0)]
5099//             );
5100//         });
5101//         window.simulate_prompt_answer(0, cx);
5102
5103//         cx.foreground().run_until_parked();
5104//         left_pane.read_with(cx, |pane, cx| {
5105//             assert_eq!(
5106//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
5107//                 &[ProjectEntryId::from_proto(2)]
5108//             );
5109//         });
5110//         window.simulate_prompt_answer(0, cx);
5111
5112//         cx.foreground().run_until_parked();
5113//         close.await.unwrap();
5114//         left_pane.read_with(cx, |pane, _| {
5115//             assert_eq!(pane.items_len(), 0);
5116//         });
5117//     }
5118
5119//     #[gpui::test]
5120//     async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5121//         init_test(cx);
5122
5123//         let fs = FakeFs::new(cx.background());
5124
5125//         let project = Project::test(fs, [], cx).await;
5126//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5127//         let workspace = window.root(cx);
5128//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5129
5130//         let item = window.build_view(cx, |cx| {
5131//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
5132//         });
5133//         let item_id = item.id();
5134//         workspace.update(cx, |workspace, cx| {
5135//             workspace.add_item(Box::new(item.clone()), cx);
5136//         });
5137
5138//         // Autosave on window change.
5139//         item.update(cx, |item, cx| {
5140//             cx.update_global(|settings: &mut SettingsStore, cx| {
5141//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5142//                     settings.autosave = Some(AutosaveSetting::OnWindowChange);
5143//                 })
5144//             });
5145//             item.is_dirty = true;
5146//         });
5147
5148//         // Deactivating the window saves the file.
5149//         window.simulate_deactivation(cx);
5150//         deterministic.run_until_parked();
5151//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
5152
5153//         // Autosave on focus change.
5154//         item.update(cx, |item, cx| {
5155//             cx.focus_self();
5156//             cx.update_global(|settings: &mut SettingsStore, cx| {
5157//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5158//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5159//                 })
5160//             });
5161//             item.is_dirty = true;
5162//         });
5163
5164//         // Blurring the item saves the file.
5165//         item.update(cx, |_, cx| cx.blur());
5166//         deterministic.run_until_parked();
5167//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
5168
5169//         // Deactivating the window still saves the file.
5170//         window.simulate_activation(cx);
5171//         item.update(cx, |item, cx| {
5172//             cx.focus_self();
5173//             item.is_dirty = true;
5174//         });
5175//         window.simulate_deactivation(cx);
5176
5177//         deterministic.run_until_parked();
5178//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5179
5180//         // Autosave after delay.
5181//         item.update(cx, |item, cx| {
5182//             cx.update_global(|settings: &mut SettingsStore, cx| {
5183//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5184//                     settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
5185//                 })
5186//             });
5187//             item.is_dirty = true;
5188//             cx.emit(TestItemEvent::Edit);
5189//         });
5190
5191//         // Delay hasn't fully expired, so the file is still dirty and unsaved.
5192//         deterministic.advance_clock(Duration::from_millis(250));
5193//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5194
5195//         // After delay expires, the file is saved.
5196//         deterministic.advance_clock(Duration::from_millis(250));
5197//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 4));
5198
5199//         // Autosave on focus change, ensuring closing the tab counts as such.
5200//         item.update(cx, |item, cx| {
5201//             cx.update_global(|settings: &mut SettingsStore, cx| {
5202//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5203//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5204//                 })
5205//             });
5206//             item.is_dirty = true;
5207//         });
5208
5209//         pane.update(cx, |pane, cx| {
5210//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5211//         })
5212//         .await
5213//         .unwrap();
5214//         assert!(!window.has_pending_prompt(cx));
5215//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5216
5217//         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
5218//         workspace.update(cx, |workspace, cx| {
5219//             workspace.add_item(Box::new(item.clone()), cx);
5220//         });
5221//         item.update(cx, |item, cx| {
5222//             item.project_items[0].update(cx, |item, _| {
5223//                 item.entry_id = None;
5224//             });
5225//             item.is_dirty = true;
5226//             cx.blur();
5227//         });
5228//         deterministic.run_until_parked();
5229//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5230
5231//         // Ensure autosave is prevented for deleted files also when closing the buffer.
5232//         let _close_items = pane.update(cx, |pane, cx| {
5233//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5234//         });
5235//         deterministic.run_until_parked();
5236//         assert!(window.has_pending_prompt(cx));
5237//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5238//     }
5239
5240//     #[gpui::test]
5241//     async fn test_pane_navigation(cx: &mut gpui::TestAppContext) {
5242//         init_test(cx);
5243
5244//         let fs = FakeFs::new(cx.background());
5245
5246//         let project = Project::test(fs, [], cx).await;
5247//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5248//         let workspace = window.root(cx);
5249
5250//         let item = window.build_view(cx, |cx| {
5251//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
5252//         });
5253//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5254//         let toolbar = pane.read_with(cx, |pane, _| pane.toolbar().clone());
5255//         let toolbar_notify_count = Rc::new(RefCell::new(0));
5256
5257//         workspace.update(cx, |workspace, cx| {
5258//             workspace.add_item(Box::new(item.clone()), cx);
5259//             let toolbar_notification_count = toolbar_notify_count.clone();
5260//             cx.observe(&toolbar, move |_, _, _| {
5261//                 *toolbar_notification_count.borrow_mut() += 1
5262//             })
5263//             .detach();
5264//         });
5265
5266//         pane.read_with(cx, |pane, _| {
5267//             assert!(!pane.can_navigate_backward());
5268//             assert!(!pane.can_navigate_forward());
5269//         });
5270
5271//         item.update(cx, |item, cx| {
5272//             item.set_state("one".to_string(), cx);
5273//         });
5274
5275//         // Toolbar must be notified to re-render the navigation buttons
5276//         assert_eq!(*toolbar_notify_count.borrow(), 1);
5277
5278//         pane.read_with(cx, |pane, _| {
5279//             assert!(pane.can_navigate_backward());
5280//             assert!(!pane.can_navigate_forward());
5281//         });
5282
5283//         workspace
5284//             .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
5285//             .await
5286//             .unwrap();
5287
5288//         assert_eq!(*toolbar_notify_count.borrow(), 3);
5289//         pane.read_with(cx, |pane, _| {
5290//             assert!(!pane.can_navigate_backward());
5291//             assert!(pane.can_navigate_forward());
5292//         });
5293//     }
5294
5295//     #[gpui::test]
5296//     async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) {
5297//         init_test(cx);
5298//         let fs = FakeFs::new(cx.background());
5299
5300//         let project = Project::test(fs, [], cx).await;
5301//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5302//         let workspace = window.root(cx);
5303
5304//         let panel = workspace.update(cx, |workspace, cx| {
5305//             let panel = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5306//             workspace.add_panel(panel.clone(), cx);
5307
5308//             workspace
5309//                 .right_dock()
5310//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5311
5312//             panel
5313//         });
5314
5315//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5316//         pane.update(cx, |pane, cx| {
5317//             let item = cx.build_view(|_| TestItem::new());
5318//             pane.add_item(Box::new(item), true, true, None, cx);
5319//         });
5320
5321//         // Transfer focus from center to panel
5322//         workspace.update(cx, |workspace, cx| {
5323//             workspace.toggle_panel_focus::<TestPanel>(cx);
5324//         });
5325
5326//         workspace.read_with(cx, |workspace, cx| {
5327//             assert!(workspace.right_dock().read(cx).is_open());
5328//             assert!(!panel.is_zoomed(cx));
5329//             assert!(panel.has_focus(cx));
5330//         });
5331
5332//         // Transfer focus from panel to center
5333//         workspace.update(cx, |workspace, cx| {
5334//             workspace.toggle_panel_focus::<TestPanel>(cx);
5335//         });
5336
5337//         workspace.read_with(cx, |workspace, cx| {
5338//             assert!(workspace.right_dock().read(cx).is_open());
5339//             assert!(!panel.is_zoomed(cx));
5340//             assert!(!panel.has_focus(cx));
5341//         });
5342
5343//         // Close the dock
5344//         workspace.update(cx, |workspace, cx| {
5345//             workspace.toggle_dock(DockPosition::Right, cx);
5346//         });
5347
5348//         workspace.read_with(cx, |workspace, cx| {
5349//             assert!(!workspace.right_dock().read(cx).is_open());
5350//             assert!(!panel.is_zoomed(cx));
5351//             assert!(!panel.has_focus(cx));
5352//         });
5353
5354//         // Open the dock
5355//         workspace.update(cx, |workspace, cx| {
5356//             workspace.toggle_dock(DockPosition::Right, cx);
5357//         });
5358
5359//         workspace.read_with(cx, |workspace, cx| {
5360//             assert!(workspace.right_dock().read(cx).is_open());
5361//             assert!(!panel.is_zoomed(cx));
5362//             assert!(panel.has_focus(cx));
5363//         });
5364
5365//         // Focus and zoom panel
5366//         panel.update(cx, |panel, cx| {
5367//             cx.focus_self();
5368//             panel.set_zoomed(true, cx)
5369//         });
5370
5371//         workspace.read_with(cx, |workspace, cx| {
5372//             assert!(workspace.right_dock().read(cx).is_open());
5373//             assert!(panel.is_zoomed(cx));
5374//             assert!(panel.has_focus(cx));
5375//         });
5376
5377//         // Transfer focus to the center closes the dock
5378//         workspace.update(cx, |workspace, cx| {
5379//             workspace.toggle_panel_focus::<TestPanel>(cx);
5380//         });
5381
5382//         workspace.read_with(cx, |workspace, cx| {
5383//             assert!(!workspace.right_dock().read(cx).is_open());
5384//             assert!(panel.is_zoomed(cx));
5385//             assert!(!panel.has_focus(cx));
5386//         });
5387
5388//         // Transferring focus back to the panel keeps it zoomed
5389//         workspace.update(cx, |workspace, cx| {
5390//             workspace.toggle_panel_focus::<TestPanel>(cx);
5391//         });
5392
5393//         workspace.read_with(cx, |workspace, cx| {
5394//             assert!(workspace.right_dock().read(cx).is_open());
5395//             assert!(panel.is_zoomed(cx));
5396//             assert!(panel.has_focus(cx));
5397//         });
5398
5399//         // Close the dock while it is zoomed
5400//         workspace.update(cx, |workspace, cx| {
5401//             workspace.toggle_dock(DockPosition::Right, cx)
5402//         });
5403
5404//         workspace.read_with(cx, |workspace, cx| {
5405//             assert!(!workspace.right_dock().read(cx).is_open());
5406//             assert!(panel.is_zoomed(cx));
5407//             assert!(workspace.zoomed.is_none());
5408//             assert!(!panel.has_focus(cx));
5409//         });
5410
5411//         // Opening the dock, when it's zoomed, retains focus
5412//         workspace.update(cx, |workspace, cx| {
5413//             workspace.toggle_dock(DockPosition::Right, cx)
5414//         });
5415
5416//         workspace.read_with(cx, |workspace, cx| {
5417//             assert!(workspace.right_dock().read(cx).is_open());
5418//             assert!(panel.is_zoomed(cx));
5419//             assert!(workspace.zoomed.is_some());
5420//             assert!(panel.has_focus(cx));
5421//         });
5422
5423//         // Unzoom and close the panel, zoom the active pane.
5424//         panel.update(cx, |panel, cx| panel.set_zoomed(false, cx));
5425//         workspace.update(cx, |workspace, cx| {
5426//             workspace.toggle_dock(DockPosition::Right, cx)
5427//         });
5428//         pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx));
5429
5430//         // Opening a dock unzooms the pane.
5431//         workspace.update(cx, |workspace, cx| {
5432//             workspace.toggle_dock(DockPosition::Right, cx)
5433//         });
5434//         workspace.read_with(cx, |workspace, cx| {
5435//             let pane = pane.read(cx);
5436//             assert!(!pane.is_zoomed());
5437//             assert!(!pane.has_focus());
5438//             assert!(workspace.right_dock().read(cx).is_open());
5439//             assert!(workspace.zoomed.is_none());
5440//         });
5441//     }
5442
5443//     #[gpui::test]
5444//     async fn test_panels(cx: &mut gpui::TestAppContext) {
5445//         init_test(cx);
5446//         let fs = FakeFs::new(cx.background());
5447
5448//         let project = Project::test(fs, [], cx).await;
5449//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5450//         let workspace = window.root(cx);
5451
5452//         let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| {
5453//             // Add panel_1 on the left, panel_2 on the right.
5454//             let panel_1 = cx.build_view(|_| TestPanel::new(DockPosition::Left));
5455//             workspace.add_panel(panel_1.clone(), cx);
5456//             workspace
5457//                 .left_dock()
5458//                 .update(cx, |left_dock, cx| left_dock.set_open(true, cx));
5459//             let panel_2 = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5460//             workspace.add_panel(panel_2.clone(), cx);
5461//             workspace
5462//                 .right_dock()
5463//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5464
5465//             let left_dock = workspace.left_dock();
5466//             assert_eq!(
5467//                 left_dock.read(cx).visible_panel().unwrap().id(),
5468//                 panel_1.id()
5469//             );
5470//             assert_eq!(
5471//                 left_dock.read(cx).active_panel_size(cx).unwrap(),
5472//                 panel_1.size(cx)
5473//             );
5474
5475//             left_dock.update(cx, |left_dock, cx| {
5476//                 left_dock.resize_active_panel(Some(1337.), cx)
5477//             });
5478//             assert_eq!(
5479//                 workspace
5480//                     .right_dock()
5481//                     .read(cx)
5482//                     .visible_panel()
5483//                     .unwrap()
5484//                     .id(),
5485//                 panel_2.id()
5486//             );
5487
5488//             (panel_1, panel_2)
5489//         });
5490
5491//         // Move panel_1 to the right
5492//         panel_1.update(cx, |panel_1, cx| {
5493//             panel_1.set_position(DockPosition::Right, cx)
5494//         });
5495
5496//         workspace.update(cx, |workspace, cx| {
5497//             // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right.
5498//             // Since it was the only panel on the left, the left dock should now be closed.
5499//             assert!(!workspace.left_dock().read(cx).is_open());
5500//             assert!(workspace.left_dock().read(cx).visible_panel().is_none());
5501//             let right_dock = workspace.right_dock();
5502//             assert_eq!(
5503//                 right_dock.read(cx).visible_panel().unwrap().id(),
5504//                 panel_1.id()
5505//             );
5506//             assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5507
5508//             // Now we move panel_2Β to the left
5509//             panel_2.set_position(DockPosition::Left, cx);
5510//         });
5511
5512//         workspace.update(cx, |workspace, cx| {
5513//             // Since panel_2 was not visible on the right, we don't open the left dock.
5514//             assert!(!workspace.left_dock().read(cx).is_open());
5515//             // And the right dock is unaffected in it's displaying of panel_1
5516//             assert!(workspace.right_dock().read(cx).is_open());
5517//             assert_eq!(
5518//                 workspace
5519//                     .right_dock()
5520//                     .read(cx)
5521//                     .visible_panel()
5522//                     .unwrap()
5523//                     .id(),
5524//                 panel_1.id()
5525//             );
5526//         });
5527
5528//         // Move panel_1 back to the left
5529//         panel_1.update(cx, |panel_1, cx| {
5530//             panel_1.set_position(DockPosition::Left, cx)
5531//         });
5532
5533//         workspace.update(cx, |workspace, cx| {
5534//             // Since panel_1 was visible on the right, we open the left dock and make panel_1 active.
5535//             let left_dock = workspace.left_dock();
5536//             assert!(left_dock.read(cx).is_open());
5537//             assert_eq!(
5538//                 left_dock.read(cx).visible_panel().unwrap().id(),
5539//                 panel_1.id()
5540//             );
5541//             assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5542//             // And right the dock should be closed as it no longer has any panels.
5543//             assert!(!workspace.right_dock().read(cx).is_open());
5544
5545//             // Now we move panel_1 to the bottom
5546//             panel_1.set_position(DockPosition::Bottom, cx);
5547//         });
5548
5549//         workspace.update(cx, |workspace, cx| {
5550//             // Since panel_1 was visible on the left, we close the left dock.
5551//             assert!(!workspace.left_dock().read(cx).is_open());
5552//             // The bottom dock is sized based on the panel's default size,
5553//             // since the panel orientation changed from vertical to horizontal.
5554//             let bottom_dock = workspace.bottom_dock();
5555//             assert_eq!(
5556//                 bottom_dock.read(cx).active_panel_size(cx).unwrap(),
5557//                 panel_1.size(cx),
5558//             );
5559//             // Close bottom dock and move panel_1 back to the left.
5560//             bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx));
5561//             panel_1.set_position(DockPosition::Left, cx);
5562//         });
5563
5564//         // Emit activated event on panel 1
5565//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Activated));
5566
5567//         // Now the left dock is open and panel_1 is active and focused.
5568//         workspace.read_with(cx, |workspace, cx| {
5569//             let left_dock = workspace.left_dock();
5570//             assert!(left_dock.read(cx).is_open());
5571//             assert_eq!(
5572//                 left_dock.read(cx).visible_panel().unwrap().id(),
5573//                 panel_1.id()
5574//             );
5575//             assert!(panel_1.is_focused(cx));
5576//         });
5577
5578//         // Emit closed event on panel 2, which is not active
5579//         panel_2.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5580
5581//         // Wo don't close the left dock, because panel_2 wasn't the active panel
5582//         workspace.read_with(cx, |workspace, cx| {
5583//             let left_dock = workspace.left_dock();
5584//             assert!(left_dock.read(cx).is_open());
5585//             assert_eq!(
5586//                 left_dock.read(cx).visible_panel().unwrap().id(),
5587//                 panel_1.id()
5588//             );
5589//         });
5590
5591//         // Emitting a ZoomIn event shows the panel as zoomed.
5592//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn));
5593//         workspace.read_with(cx, |workspace, _| {
5594//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5595//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Left));
5596//         });
5597
5598//         // Move panel to another dock while it is zoomed
5599//         panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx));
5600//         workspace.read_with(cx, |workspace, _| {
5601//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5602//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5603//         });
5604
5605//         // If focus is transferred to another view that's not a panel or another pane, we still show
5606//         // the panel as zoomed.
5607//         let focus_receiver = window.build_view(cx, |_| EmptyView);
5608//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5609//         workspace.read_with(cx, |workspace, _| {
5610//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5611//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5612//         });
5613
5614//         // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed.
5615//         workspace.update(cx, |_, cx| cx.focus_self());
5616//         workspace.read_with(cx, |workspace, _| {
5617//             assert_eq!(workspace.zoomed, None);
5618//             assert_eq!(workspace.zoomed_position, None);
5619//         });
5620
5621//         // If focus is transferred again to another view that's not a panel or a pane, we won't
5622//         // show the panel as zoomed because it wasn't zoomed before.
5623//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5624//         workspace.read_with(cx, |workspace, _| {
5625//             assert_eq!(workspace.zoomed, None);
5626//             assert_eq!(workspace.zoomed_position, None);
5627//         });
5628
5629//         // When focus is transferred back to the panel, it is zoomed again.
5630//         panel_1.update(cx, |_, cx| cx.focus_self());
5631//         workspace.read_with(cx, |workspace, _| {
5632//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5633//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5634//         });
5635
5636//         // Emitting a ZoomOut event unzooms the panel.
5637//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut));
5638//         workspace.read_with(cx, |workspace, _| {
5639//             assert_eq!(workspace.zoomed, None);
5640//             assert_eq!(workspace.zoomed_position, None);
5641//         });
5642
5643//         // Emit closed event on panel 1, which is active
5644//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5645
5646//         // Now the left dock is closed, because panel_1 was the active panel
5647//         workspace.read_with(cx, |workspace, cx| {
5648//             let right_dock = workspace.right_dock();
5649//             assert!(!right_dock.read(cx).is_open());
5650//         });
5651//     }
5652
5653//     pub fn init_test(cx: &mut TestAppContext) {
5654//         cx.foreground().forbid_parking();
5655//         cx.update(|cx| {
5656//             cx.set_global(SettingsStore::test(cx));
5657//             theme::init((), cx);
5658//             language::init(cx);
5659//             crate::init_settings(cx);
5660//             Project::init_settings(cx);
5661//         });
5662//     }
5663// }