workspace2.rs

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