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