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