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