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