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