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            .border()
3715            .border_color(cx.theme().colors().border)
3716            .children(self.titlebar_item.clone())
3717            .child(
3718                div()
3719                    .id("workspace")
3720                    .relative()
3721                    .flex_1()
3722                    .w_full()
3723                    .flex()
3724                    .overflow_hidden()
3725                    .border_t()
3726                    .border_b()
3727                    .border_color(cx.theme().colors().border)
3728                    .child(self.modal_layer.clone())
3729                    .child(
3730                        div()
3731                            .flex()
3732                            .flex_row()
3733                            .flex_1()
3734                            .h_full()
3735                            // Left Dock
3736                            .child(
3737                                div()
3738                                    .flex()
3739                                    .flex_none()
3740                                    .overflow_hidden()
3741                                    .child(self.left_dock.clone()),
3742                            )
3743                            // Panes
3744                            .child(
3745                                div()
3746                                    .flex()
3747                                    .flex_col()
3748                                    .flex_1()
3749                                    .child(self.center.render(
3750                                        &self.project,
3751                                        &self.follower_states,
3752                                        &self.active_pane,
3753                                        self.zoomed.as_ref(),
3754                                        &self.app_state,
3755                                        cx,
3756                                    ))
3757                                    .child(self.bottom_dock.clone()),
3758                            )
3759                            // Right Dock
3760                            .child(
3761                                div()
3762                                    .flex()
3763                                    .flex_none()
3764                                    .overflow_hidden()
3765                                    .child(self.right_dock.clone()),
3766                            ),
3767                    )
3768                    .children(self.render_notifications(cx)),
3769            )
3770            .child(self.status_bar.clone())
3771    }
3772}
3773
3774// impl View for Workspace {
3775
3776//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
3777//         let theme = theme::current(cx).clone();
3778//         Stack::new()
3779//             .with_child(
3780//                 Flex::column()
3781//                     .with_child(self.render_titlebar(&theme, cx))
3782//                     .with_child(
3783//                         Stack::new()
3784//                             .with_child({
3785//                                 let project = self.project.clone();
3786//                                 Flex::row()
3787//                                     .with_children(self.render_dock(DockPosition::Left, cx))
3788//                                     .with_child(
3789//                                         Flex::column()
3790//                                             .with_child(
3791//                                                 FlexItem::new(
3792//                                                     self.center.render(
3793//                                                         &project,
3794//                                                         &theme,
3795//                                                         &self.follower_states,
3796//                                                         self.active_call(),
3797//                                                         self.active_pane(),
3798//                                                         self.zoomed
3799//                                                             .as_ref()
3800//                                                             .and_then(|zoomed| zoomed.upgrade(cx))
3801//                                                             .as_ref(),
3802//                                                         &self.app_state,
3803//                                                         cx,
3804//                                                     ),
3805//                                                 )
3806//                                                 .flex(1., true),
3807//                                             )
3808//                                             .with_children(
3809//                                                 self.render_dock(DockPosition::Bottom, cx),
3810//                                             )
3811//                                             .flex(1., true),
3812//                                     )
3813//                                     .with_children(self.render_dock(DockPosition::Right, cx))
3814//                             })
3815//                             .with_child(Overlay::new(
3816//                                 Stack::new()
3817//                                     .with_children(self.zoomed.as_ref().and_then(|zoomed| {
3818//                                         enum ZoomBackground {}
3819//                                         let zoomed = zoomed.upgrade(cx)?;
3820
3821//                                         let mut foreground_style =
3822//                                             theme.workspace.zoomed_pane_foreground;
3823//                                         if let Some(zoomed_dock_position) = self.zoomed_position {
3824//                                             foreground_style =
3825//                                                 theme.workspace.zoomed_panel_foreground;
3826//                                             let margin = foreground_style.margin.top;
3827//                                             let border = foreground_style.border.top;
3828
3829//                                             // Only include a margin and border on the opposite side.
3830//                                             foreground_style.margin.top = 0.;
3831//                                             foreground_style.margin.left = 0.;
3832//                                             foreground_style.margin.bottom = 0.;
3833//                                             foreground_style.margin.right = 0.;
3834//                                             foreground_style.border.top = false;
3835//                                             foreground_style.border.left = false;
3836//                                             foreground_style.border.bottom = false;
3837//                                             foreground_style.border.right = false;
3838//                                             match zoomed_dock_position {
3839//                                                 DockPosition::Left => {
3840//                                                     foreground_style.margin.right = margin;
3841//                                                     foreground_style.border.right = border;
3842//                                                 }
3843//                                                 DockPosition::Right => {
3844//                                                     foreground_style.margin.left = margin;
3845//                                                     foreground_style.border.left = border;
3846//                                                 }
3847//                                                 DockPosition::Bottom => {
3848//                                                     foreground_style.margin.top = margin;
3849//                                                     foreground_style.border.top = border;
3850//                                                 }
3851//                                             }
3852//                                         }
3853
3854//                                         Some(
3855//                                             ChildView::new(&zoomed, cx)
3856//                                                 .contained()
3857//                                                 .with_style(foreground_style)
3858//                                                 .aligned()
3859//                                                 .contained()
3860//                                                 .with_style(theme.workspace.zoomed_background)
3861//                                                 .mouse::<ZoomBackground>(0)
3862//                                                 .capture_all()
3863//                                                 .on_down(
3864//                                                     MouseButton::Left,
3865//                                                     |_, this: &mut Self, cx| {
3866//                                                         this.zoom_out(cx);
3867//                                                     },
3868//                                                 ),
3869//                                         )
3870//                                     }))
3871//                                     .with_children(self.modal.as_ref().map(|modal| {
3872//                                         // Prevent clicks within the modal from falling
3873//                                         // through to the rest of the workspace.
3874//                                         enum ModalBackground {}
3875//                                         MouseEventHandler::new::<ModalBackground, _>(
3876//                                             0,
3877//                                             cx,
3878//                                             |_, cx| ChildView::new(modal.view.as_any(), cx),
3879//                                         )
3880//                                         .on_click(MouseButton::Left, |_, _, _| {})
3881//                                         .contained()
3882//                                         .with_style(theme.workspace.modal)
3883//                                         .aligned()
3884//                                         .top()
3885//                                     }))
3886//                                     .with_children(self.render_notifications(&theme.workspace, cx)),
3887//                             ))
3888//                             .provide_resize_bounds::<WorkspaceBounds>()
3889//                             .flex(1.0, true),
3890//                     )
3891//                     .with_child(ChildView::new(&self.status_bar, cx))
3892//                     .contained()
3893//                     .with_background_color(theme.workspace.background),
3894//             )
3895//             .with_children(DragAndDrop::render(cx))
3896//             .with_children(self.render_disconnected_overlay(cx))
3897//             .into_any_named("workspace")
3898//     }
3899
3900//     fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
3901//         DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
3902//     }
3903// }
3904
3905impl WorkspaceStore {
3906    pub fn new(client: Arc<Client>, _cx: &mut ModelContext<Self>) -> Self {
3907        Self {
3908            workspaces: Default::default(),
3909            followers: Default::default(),
3910            _subscriptions: vec![],
3911            //     client.add_request_handler(cx.weak_model(), Self::handle_follow),
3912            //     client.add_message_handler(cx.weak_model(), Self::handle_unfollow),
3913            //     client.add_message_handler(cx.weak_model(), Self::handle_update_followers),
3914            // ],
3915            client,
3916        }
3917    }
3918
3919    pub fn update_followers(
3920        &self,
3921        project_id: Option<u64>,
3922        room_id: u64,
3923        update: proto::update_followers::Variant,
3924        cx: &AppContext,
3925    ) -> Option<()> {
3926        let follower_ids: Vec<_> = self
3927            .followers
3928            .iter()
3929            .filter_map(|follower| {
3930                if follower.project_id == project_id || project_id.is_none() {
3931                    Some(follower.peer_id.into())
3932                } else {
3933                    None
3934                }
3935            })
3936            .collect();
3937        if follower_ids.is_empty() {
3938            return None;
3939        }
3940        self.client
3941            .send(proto::UpdateFollowers {
3942                room_id,
3943                project_id,
3944                follower_ids,
3945                variant: Some(update),
3946            })
3947            .log_err()
3948    }
3949
3950    pub async fn handle_follow(
3951        this: Model<Self>,
3952        envelope: TypedEnvelope<proto::Follow>,
3953        _: Arc<Client>,
3954        mut cx: AsyncAppContext,
3955    ) -> Result<proto::FollowResponse> {
3956        this.update(&mut cx, |this, cx| {
3957            let follower = Follower {
3958                project_id: envelope.payload.project_id,
3959                peer_id: envelope.original_sender_id()?,
3960            };
3961            let mut response = proto::FollowResponse::default();
3962            let active_project = this
3963                .workspaces
3964                .iter()
3965                .next()
3966                .and_then(|workspace| {
3967                    workspace
3968                        .read_with(cx, |this, cx| this.call_handler.active_project(cx))
3969                        .log_err()
3970                })
3971                .flatten();
3972            for workspace in &this.workspaces {
3973                workspace
3974                    .update(cx, |workspace, cx| {
3975                        let handler_response = workspace.handle_follow(follower.project_id, cx);
3976                        if response.views.is_empty() {
3977                            response.views = handler_response.views;
3978                        } else {
3979                            response.views.extend_from_slice(&handler_response.views);
3980                        }
3981
3982                        if let Some(active_view_id) = handler_response.active_view_id.clone() {
3983                            if response.active_view_id.is_none()
3984                                || Some(workspace.project.downgrade()) == active_project
3985                            {
3986                                response.active_view_id = Some(active_view_id);
3987                            }
3988                        }
3989                    })
3990                    .ok();
3991            }
3992
3993            if let Err(ix) = this.followers.binary_search(&follower) {
3994                this.followers.insert(ix, follower);
3995            }
3996
3997            Ok(response)
3998        })?
3999    }
4000
4001    async fn handle_unfollow(
4002        model: Model<Self>,
4003        envelope: TypedEnvelope<proto::Unfollow>,
4004        _: Arc<Client>,
4005        mut cx: AsyncAppContext,
4006    ) -> Result<()> {
4007        model.update(&mut cx, |this, _| {
4008            let follower = Follower {
4009                project_id: envelope.payload.project_id,
4010                peer_id: envelope.original_sender_id()?,
4011            };
4012            if let Ok(ix) = this.followers.binary_search(&follower) {
4013                this.followers.remove(ix);
4014            }
4015            Ok(())
4016        })?
4017    }
4018
4019    async fn handle_update_followers(
4020        this: Model<Self>,
4021        envelope: TypedEnvelope<proto::UpdateFollowers>,
4022        _: Arc<Client>,
4023        mut cx: AsyncWindowContext,
4024    ) -> Result<()> {
4025        let leader_id = envelope.original_sender_id()?;
4026        let update = envelope.payload;
4027
4028        this.update(&mut cx, |this, cx| {
4029            for workspace in &this.workspaces {
4030                workspace.update(cx, |workspace, cx| {
4031                    let project_id = workspace.project.read(cx).remote_id();
4032                    if update.project_id != project_id && update.project_id.is_some() {
4033                        return;
4034                    }
4035                    workspace.handle_update_followers(leader_id, update.clone(), cx);
4036                })?;
4037            }
4038            Ok(())
4039        })?
4040    }
4041}
4042
4043impl ViewId {
4044    pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
4045        Ok(Self {
4046            creator: message
4047                .creator
4048                .ok_or_else(|| anyhow!("creator is missing"))?,
4049            id: message.id,
4050        })
4051    }
4052
4053    pub(crate) fn to_proto(&self) -> proto::ViewId {
4054        proto::ViewId {
4055            creator: Some(self.creator),
4056            id: self.id,
4057        }
4058    }
4059}
4060
4061pub trait WorkspaceHandle {
4062    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
4063}
4064
4065impl WorkspaceHandle for View<Workspace> {
4066    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
4067        self.read(cx)
4068            .worktrees(cx)
4069            .flat_map(|worktree| {
4070                let worktree_id = worktree.read(cx).id();
4071                worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
4072                    worktree_id,
4073                    path: f.path.clone(),
4074                })
4075            })
4076            .collect::<Vec<_>>()
4077    }
4078}
4079
4080impl std::fmt::Debug for OpenPaths {
4081    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4082        f.debug_struct("OpenPaths")
4083            .field("paths", &self.paths)
4084            .finish()
4085    }
4086}
4087
4088pub struct WorkspaceCreated(pub WeakView<Workspace>);
4089
4090pub fn activate_workspace_for_project(
4091    cx: &mut AppContext,
4092    predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static,
4093) -> Option<WindowHandle<Workspace>> {
4094    for window in cx.windows() {
4095        let Some(workspace) = window.downcast::<Workspace>() else {
4096            continue;
4097        };
4098
4099        let predicate = workspace
4100            .update(cx, |workspace, cx| {
4101                let project = workspace.project.read(cx);
4102                if predicate(project, cx) {
4103                    cx.activate_window();
4104                    true
4105                } else {
4106                    false
4107                }
4108            })
4109            .log_err()
4110            .unwrap_or(false);
4111
4112        if predicate {
4113            return Some(workspace);
4114        }
4115    }
4116
4117    None
4118}
4119
4120pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
4121    DB.last_workspace().await.log_err().flatten()
4122}
4123
4124// async fn join_channel_internal(
4125//     channel_id: u64,
4126//     app_state: &Arc<AppState>,
4127//     requesting_window: Option<WindowHandle<Workspace>>,
4128//     active_call: &ModelHandle<ActiveCall>,
4129//     cx: &mut AsyncAppContext,
4130// ) -> Result<bool> {
4131//     let (should_prompt, open_room) = active_call.read_with(cx, |active_call, cx| {
4132//         let Some(room) = active_call.room().map(|room| room.read(cx)) else {
4133//             return (false, None);
4134//         };
4135
4136//         let already_in_channel = room.channel_id() == Some(channel_id);
4137//         let should_prompt = room.is_sharing_project()
4138//             && room.remote_participants().len() > 0
4139//             && !already_in_channel;
4140//         let open_room = if already_in_channel {
4141//             active_call.room().cloned()
4142//         } else {
4143//             None
4144//         };
4145//         (should_prompt, open_room)
4146//     });
4147
4148//     if let Some(room) = open_room {
4149//         let task = room.update(cx, |room, cx| {
4150//             if let Some((project, host)) = room.most_active_project(cx) {
4151//                 return Some(join_remote_project(project, host, app_state.clone(), cx));
4152//             }
4153
4154//             None
4155//         });
4156//         if let Some(task) = task {
4157//             task.await?;
4158//         }
4159//         return anyhow::Ok(true);
4160//     }
4161
4162//     if should_prompt {
4163//         if let Some(workspace) = requesting_window {
4164//             if let Some(window) = workspace.update(cx, |cx| cx.window()) {
4165//                 let answer = window.prompt(
4166//                     PromptLevel::Warning,
4167//                     "Leaving this call will unshare your current project.\nDo you want to switch channels?",
4168//                     &["Yes, Join Channel", "Cancel"],
4169//                     cx,
4170//                 );
4171
4172//                 if let Some(mut answer) = answer {
4173//                     if answer.next().await == Some(1) {
4174//                         return Ok(false);
4175//                     }
4176//                 }
4177//             } else {
4178//                 return Ok(false); // unreachable!() hopefully
4179//             }
4180//         } else {
4181//             return Ok(false); // unreachable!() hopefully
4182//         }
4183//     }
4184
4185//     let client = cx.read(|cx| active_call.read(cx).client());
4186
4187//     let mut client_status = client.status();
4188
4189//     // this loop will terminate within client::CONNECTION_TIMEOUT seconds.
4190//     'outer: loop {
4191//         let Some(status) = client_status.recv().await else {
4192//             return Err(anyhow!("error connecting"));
4193//         };
4194
4195//         match status {
4196//             Status::Connecting
4197//             | Status::Authenticating
4198//             | Status::Reconnecting
4199//             | Status::Reauthenticating => continue,
4200//             Status::Connected { .. } => break 'outer,
4201//             Status::SignedOut => return Err(anyhow!("not signed in")),
4202//             Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
4203//             Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
4204//                 return Err(anyhow!("zed is offline"))
4205//             }
4206//         }
4207//     }
4208
4209//     let room = active_call
4210//         .update(cx, |active_call, cx| {
4211//             active_call.join_channel(channel_id, cx)
4212//         })
4213//         .await?;
4214
4215//     room.update(cx, |room, _| room.room_update_completed())
4216//         .await;
4217
4218//     let task = room.update(cx, |room, cx| {
4219//         if let Some((project, host)) = room.most_active_project(cx) {
4220//             return Some(join_remote_project(project, host, app_state.clone(), cx));
4221//         }
4222
4223//         None
4224//     });
4225//     if let Some(task) = task {
4226//         task.await?;
4227//         return anyhow::Ok(true);
4228//     }
4229//     anyhow::Ok(false)
4230// }
4231
4232// pub fn join_channel(
4233//     channel_id: u64,
4234//     app_state: Arc<AppState>,
4235//     requesting_window: Option<WindowHandle<Workspace>>,
4236//     cx: &mut AppContext,
4237// ) -> Task<Result<()>> {
4238//     let active_call = ActiveCall::global(cx);
4239//     cx.spawn(|mut cx| async move {
4240//         let result = join_channel_internal(
4241//             channel_id,
4242//             &app_state,
4243//             requesting_window,
4244//             &active_call,
4245//             &mut cx,
4246//         )
4247//         .await;
4248
4249//         // join channel succeeded, and opened a window
4250//         if matches!(result, Ok(true)) {
4251//             return anyhow::Ok(());
4252//         }
4253
4254//         if requesting_window.is_some() {
4255//             return anyhow::Ok(());
4256//         }
4257
4258//         // find an existing workspace to focus and show call controls
4259//         let mut active_window = activate_any_workspace_window(&mut cx);
4260//         if active_window.is_none() {
4261//             // no open workspaces, make one to show the error in (blergh)
4262//             cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
4263//                 .await;
4264//         }
4265
4266//         active_window = activate_any_workspace_window(&mut cx);
4267//         if active_window.is_none() {
4268//             return result.map(|_| ()); // unreachable!() assuming new_local always opens a window
4269//         }
4270
4271//         if let Err(err) = result {
4272//             let prompt = active_window.unwrap().prompt(
4273//                 PromptLevel::Critical,
4274//                 &format!("Failed to join channel: {}", err),
4275//                 &["Ok"],
4276//                 &mut cx,
4277//             );
4278//             if let Some(mut prompt) = prompt {
4279//                 prompt.next().await;
4280//             } else {
4281//                 return Err(err);
4282//             }
4283//         }
4284
4285//         // return ok, we showed the error to the user.
4286//         return anyhow::Ok(());
4287//     })
4288// }
4289
4290// pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
4291//     for window in cx.windows() {
4292//         let found = window.update(cx, |cx| {
4293//             let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
4294//             if is_workspace {
4295//                 cx.activate_window();
4296//             }
4297//             is_workspace
4298//         });
4299//         if found == Some(true) {
4300//             return Some(window);
4301//         }
4302//     }
4303//     None
4304// }
4305
4306#[allow(clippy::type_complexity)]
4307pub fn open_paths(
4308    abs_paths: &[PathBuf],
4309    app_state: &Arc<AppState>,
4310    requesting_window: Option<WindowHandle<Workspace>>,
4311    cx: &mut AppContext,
4312) -> Task<
4313    anyhow::Result<(
4314        WindowHandle<Workspace>,
4315        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
4316    )>,
4317> {
4318    let app_state = app_state.clone();
4319    let abs_paths = abs_paths.to_vec();
4320    // Open paths in existing workspace if possible
4321    let existing = activate_workspace_for_project(cx, {
4322        let abs_paths = abs_paths.clone();
4323        move |project, cx| project.contains_paths(&abs_paths, cx)
4324    });
4325    cx.spawn(move |mut cx| async move {
4326        if let Some(existing) = existing {
4327            // // Ok((
4328            //     existing.clone(),
4329            //     cx.update_window_root(&existing, |workspace, cx| {
4330            //         workspace.open_paths(abs_paths, true, cx)
4331            //     })?
4332            //     .await,
4333            // ))
4334            todo!()
4335        } else {
4336            cx.update(move |cx| {
4337                Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
4338            })?
4339            .await
4340        }
4341    })
4342}
4343
4344pub fn open_new(
4345    app_state: &Arc<AppState>,
4346    cx: &mut AppContext,
4347    init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static + Send,
4348) -> Task<()> {
4349    let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
4350    cx.spawn(|mut cx| async move {
4351        if let Some((workspace, opened_paths)) = task.await.log_err() {
4352            workspace
4353                .update(&mut cx, |workspace, cx| {
4354                    if opened_paths.is_empty() {
4355                        init(workspace, cx)
4356                    }
4357                })
4358                .log_err();
4359        }
4360    })
4361}
4362
4363pub fn create_and_open_local_file(
4364    path: &'static Path,
4365    cx: &mut ViewContext<Workspace>,
4366    default_content: impl 'static + Send + FnOnce() -> Rope,
4367) -> Task<Result<Box<dyn ItemHandle>>> {
4368    cx.spawn(|workspace, mut cx| async move {
4369        let fs = workspace.update(&mut cx, |workspace, _| workspace.app_state().fs.clone())?;
4370        if !fs.is_file(path).await {
4371            fs.create_file(path, Default::default()).await?;
4372            fs.save(path, &default_content(), Default::default())
4373                .await?;
4374        }
4375
4376        let mut items = workspace
4377            .update(&mut cx, |workspace, cx| {
4378                workspace.with_local_workspace(cx, |workspace, cx| {
4379                    workspace.open_paths(vec![path.to_path_buf()], false, cx)
4380                })
4381            })?
4382            .await?
4383            .await;
4384
4385        let item = items.pop().flatten();
4386        item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
4387    })
4388}
4389
4390// pub fn join_remote_project(
4391//     project_id: u64,
4392//     follow_user_id: u64,
4393//     app_state: Arc<AppState>,
4394//     cx: &mut AppContext,
4395// ) -> Task<Result<()>> {
4396//     cx.spawn(|mut cx| async move {
4397//         let windows = cx.windows();
4398//         let existing_workspace = windows.into_iter().find_map(|window| {
4399//             window.downcast::<Workspace>().and_then(|window| {
4400//                 window
4401//                     .read_root_with(&cx, |workspace, cx| {
4402//                         if workspace.project().read(cx).remote_id() == Some(project_id) {
4403//                             Some(cx.handle().downgrade())
4404//                         } else {
4405//                             None
4406//                         }
4407//                     })
4408//                     .unwrap_or(None)
4409//             })
4410//         });
4411
4412//         let workspace = if let Some(existing_workspace) = existing_workspace {
4413//             existing_workspace
4414//         } else {
4415//             let active_call = cx.read(ActiveCall::global);
4416//             let room = active_call
4417//                 .read_with(&cx, |call, _| call.room().cloned())
4418//                 .ok_or_else(|| anyhow!("not in a call"))?;
4419//             let project = room
4420//                 .update(&mut cx, |room, cx| {
4421//                     room.join_project(
4422//                         project_id,
4423//                         app_state.languages.clone(),
4424//                         app_state.fs.clone(),
4425//                         cx,
4426//                     )
4427//                 })
4428//                 .await?;
4429
4430//             let window_bounds_override = window_bounds_env_override(&cx);
4431//             let window = cx.add_window(
4432//                 (app_state.build_window_options)(
4433//                     window_bounds_override,
4434//                     None,
4435//                     cx.platform().as_ref(),
4436//                 ),
4437//                 |cx| Workspace::new(0, project, app_state.clone(), cx),
4438//             );
4439//             let workspace = window.root(&cx).unwrap();
4440//             (app_state.initialize_workspace)(
4441//                 workspace.downgrade(),
4442//                 false,
4443//                 app_state.clone(),
4444//                 cx.clone(),
4445//             )
4446//             .await
4447//             .log_err();
4448
4449//             workspace.downgrade()
4450//         };
4451
4452//         workspace.window().activate(&mut cx);
4453//         cx.platform().activate(true);
4454
4455//         workspace.update(&mut cx, |workspace, cx| {
4456//             if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
4457//                 let follow_peer_id = room
4458//                     .read(cx)
4459//                     .remote_participants()
4460//                     .iter()
4461//                     .find(|(_, participant)| participant.user.id == follow_user_id)
4462//                     .map(|(_, p)| p.peer_id)
4463//                     .or_else(|| {
4464//                         // If we couldn't follow the given user, follow the host instead.
4465//                         let collaborator = workspace
4466//                             .project()
4467//                             .read(cx)
4468//                             .collaborators()
4469//                             .values()
4470//                             .find(|collaborator| collaborator.replica_id == 0)?;
4471//                         Some(collaborator.peer_id)
4472//                     });
4473
4474//                 if let Some(follow_peer_id) = follow_peer_id {
4475//                     workspace
4476//                         .follow(follow_peer_id, cx)
4477//                         .map(|follow| follow.detach_and_log_err(cx));
4478//                 }
4479//             }
4480//         })?;
4481
4482//         anyhow::Ok(())
4483//     })
4484// }
4485
4486pub fn restart(_: &Restart, cx: &mut AppContext) {
4487    let should_confirm = WorkspaceSettings::get_global(cx).confirm_quit;
4488    let mut workspace_windows = cx
4489        .windows()
4490        .into_iter()
4491        .filter_map(|window| window.downcast::<Workspace>())
4492        .collect::<Vec<_>>();
4493
4494    // If multiple windows have unsaved changes, and need a save prompt,
4495    // prompt in the active window before switching to a different window.
4496    workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
4497
4498    let mut prompt = None;
4499    if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
4500        prompt = window
4501            .update(cx, |_, cx| {
4502                cx.prompt(
4503                    PromptLevel::Info,
4504                    "Are you sure you want to restart?",
4505                    &["Restart", "Cancel"],
4506                )
4507            })
4508            .ok();
4509    }
4510
4511    cx.spawn(|mut cx| async move {
4512        if let Some(mut prompt) = prompt {
4513            let answer = prompt.await?;
4514            if answer != 0 {
4515                return Ok(());
4516            }
4517        }
4518
4519        // If the user cancels any save prompt, then keep the app open.
4520        for window in workspace_windows {
4521            if let Ok(should_close) = window.update(&mut cx, |workspace, cx| {
4522                workspace.prepare_to_close(true, cx)
4523            }) {
4524                if !should_close.await? {
4525                    return Ok(());
4526                }
4527            }
4528        }
4529
4530        cx.update(|cx| cx.restart())
4531    })
4532    .detach_and_log_err(cx);
4533}
4534
4535fn parse_pixel_position_env_var(value: &str) -> Option<Point<GlobalPixels>> {
4536    let mut parts = value.split(',');
4537    let x: usize = parts.next()?.parse().ok()?;
4538    let y: usize = parts.next()?.parse().ok()?;
4539    Some(point((x as f64).into(), (y as f64).into()))
4540}
4541
4542fn parse_pixel_size_env_var(value: &str) -> Option<Size<GlobalPixels>> {
4543    let mut parts = value.split(',');
4544    let width: usize = parts.next()?.parse().ok()?;
4545    let height: usize = parts.next()?.parse().ok()?;
4546    Some(size((width as f64).into(), (height as f64).into()))
4547}
4548
4549// #[cfg(test)]
4550// mod tests {
4551//     use super::*;
4552//     use crate::{
4553//         dock::test::TestPanel,
4554//         item::test::{TestItem, TestItemEvent, TestProjectItem},
4555//     };
4556//     use fs::FakeFs;
4557//     use gpui::{executor::Deterministic, test::EmptyView, TestAppContext};
4558//     use project::{Project, ProjectEntryId};
4559//     use serde_json::json;
4560//     use settings::SettingsStore;
4561//     use std::{cell::RefCell, rc::Rc};
4562
4563//     #[gpui::test]
4564//     async fn test_tab_disambiguation(cx: &mut TestAppContext) {
4565//         init_test(cx);
4566
4567//         let fs = FakeFs::new(cx.background());
4568//         let project = Project::test(fs, [], cx).await;
4569//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4570//         let workspace = window.root(cx);
4571
4572//         // Adding an item with no ambiguity renders the tab without detail.
4573//         let item1 = window.build_view(cx, |_| {
4574//             let mut item = TestItem::new();
4575//             item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
4576//             item
4577//         });
4578//         workspace.update(cx, |workspace, cx| {
4579//             workspace.add_item(Box::new(item1.clone()), cx);
4580//         });
4581//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None));
4582
4583//         // Adding an item that creates ambiguity increases the level of detail on
4584//         // both tabs.
4585//         let item2 = window.build_view(cx, |_| {
4586//             let mut item = TestItem::new();
4587//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4588//             item
4589//         });
4590//         workspace.update(cx, |workspace, cx| {
4591//             workspace.add_item(Box::new(item2.clone()), cx);
4592//         });
4593//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4594//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4595
4596//         // Adding an item that creates ambiguity increases the level of detail only
4597//         // on the ambiguous tabs. In this case, the ambiguity can't be resolved so
4598//         // we stop at the highest detail available.
4599//         let item3 = window.build_view(cx, |_| {
4600//             let mut item = TestItem::new();
4601//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4602//             item
4603//         });
4604//         workspace.update(cx, |workspace, cx| {
4605//             workspace.add_item(Box::new(item3.clone()), cx);
4606//         });
4607//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4608//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4609//         item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4610//     }
4611
4612//     #[gpui::test]
4613//     async fn test_tracking_active_path(cx: &mut TestAppContext) {
4614//         init_test(cx);
4615
4616//         let fs = FakeFs::new(cx.background());
4617//         fs.insert_tree(
4618//             "/root1",
4619//             json!({
4620//                 "one.txt": "",
4621//                 "two.txt": "",
4622//             }),
4623//         )
4624//         .await;
4625//         fs.insert_tree(
4626//             "/root2",
4627//             json!({
4628//                 "three.txt": "",
4629//             }),
4630//         )
4631//         .await;
4632
4633//         let project = Project::test(fs, ["root1".as_ref()], cx).await;
4634//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4635//         let workspace = window.root(cx);
4636//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4637//         let worktree_id = project.read_with(cx, |project, cx| {
4638//             project.worktrees().next().unwrap().read(cx).id()
4639//         });
4640
4641//         let item1 = window.build_view(cx, |cx| {
4642//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
4643//         });
4644//         let item2 = window.build_view(cx, |cx| {
4645//             TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
4646//         });
4647
4648//         // Add an item to an empty pane
4649//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx));
4650//         project.read_with(cx, |project, cx| {
4651//             assert_eq!(
4652//                 project.active_entry(),
4653//                 project
4654//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4655//                     .map(|e| e.id)
4656//             );
4657//         });
4658//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4659
4660//         // Add a second item to a non-empty pane
4661//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
4662//         assert_eq!(window.current_title(cx).as_deref(), Some("two.txt β€” root1"));
4663//         project.read_with(cx, |project, cx| {
4664//             assert_eq!(
4665//                 project.active_entry(),
4666//                 project
4667//                     .entry_for_path(&(worktree_id, "two.txt").into(), cx)
4668//                     .map(|e| e.id)
4669//             );
4670//         });
4671
4672//         // Close the active item
4673//         pane.update(cx, |pane, cx| {
4674//             pane.close_active_item(&Default::default(), cx).unwrap()
4675//         })
4676//         .await
4677//         .unwrap();
4678//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4679//         project.read_with(cx, |project, cx| {
4680//             assert_eq!(
4681//                 project.active_entry(),
4682//                 project
4683//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4684//                     .map(|e| e.id)
4685//             );
4686//         });
4687
4688//         // Add a project folder
4689//         project
4690//             .update(cx, |project, cx| {
4691//                 project.find_or_create_local_worktree("/root2", true, cx)
4692//             })
4693//             .await
4694//             .unwrap();
4695//         assert_eq!(
4696//             window.current_title(cx).as_deref(),
4697//             Some("one.txt β€” root1, root2")
4698//         );
4699
4700//         // Remove a project folder
4701//         project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
4702//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root2"));
4703//     }
4704
4705//     #[gpui::test]
4706//     async fn test_close_window(cx: &mut TestAppContext) {
4707//         init_test(cx);
4708
4709//         let fs = FakeFs::new(cx.background());
4710//         fs.insert_tree("/root", json!({ "one": "" })).await;
4711
4712//         let project = Project::test(fs, ["root".as_ref()], cx).await;
4713//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4714//         let workspace = window.root(cx);
4715
4716//         // When there are no dirty items, there's nothing to do.
4717//         let item1 = window.build_view(cx, |_| TestItem::new());
4718//         workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
4719//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4720//         assert!(task.await.unwrap());
4721
4722//         // When there are dirty untitled items, prompt to save each one. If the user
4723//         // cancels any prompt, then abort.
4724//         let item2 = window.build_view(cx, |_| TestItem::new().with_dirty(true));
4725//         let item3 = window.build_view(cx, |cx| {
4726//             TestItem::new()
4727//                 .with_dirty(true)
4728//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4729//         });
4730//         workspace.update(cx, |w, cx| {
4731//             w.add_item(Box::new(item2.clone()), cx);
4732//             w.add_item(Box::new(item3.clone()), cx);
4733//         });
4734//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4735//         cx.foreground().run_until_parked();
4736//         window.simulate_prompt_answer(2, cx); // cancel save all
4737//         cx.foreground().run_until_parked();
4738//         window.simulate_prompt_answer(2, cx); // cancel save all
4739//         cx.foreground().run_until_parked();
4740//         assert!(!window.has_pending_prompt(cx));
4741//         assert!(!task.await.unwrap());
4742//     }
4743
4744//     #[gpui::test]
4745//     async fn test_close_pane_items(cx: &mut TestAppContext) {
4746//         init_test(cx);
4747
4748//         let fs = FakeFs::new(cx.background());
4749
4750//         let project = Project::test(fs, None, cx).await;
4751//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4752//         let workspace = window.root(cx);
4753
4754//         let item1 = window.build_view(cx, |cx| {
4755//             TestItem::new()
4756//                 .with_dirty(true)
4757//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4758//         });
4759//         let item2 = window.build_view(cx, |cx| {
4760//             TestItem::new()
4761//                 .with_dirty(true)
4762//                 .with_conflict(true)
4763//                 .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
4764//         });
4765//         let item3 = window.build_view(cx, |cx| {
4766//             TestItem::new()
4767//                 .with_dirty(true)
4768//                 .with_conflict(true)
4769//                 .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
4770//         });
4771//         let item4 = window.build_view(cx, |cx| {
4772//             TestItem::new()
4773//                 .with_dirty(true)
4774//                 .with_project_items(&[TestProjectItem::new_untitled(cx)])
4775//         });
4776//         let pane = workspace.update(cx, |workspace, cx| {
4777//             workspace.add_item(Box::new(item1.clone()), cx);
4778//             workspace.add_item(Box::new(item2.clone()), cx);
4779//             workspace.add_item(Box::new(item3.clone()), cx);
4780//             workspace.add_item(Box::new(item4.clone()), cx);
4781//             workspace.active_pane().clone()
4782//         });
4783
4784//         let close_items = pane.update(cx, |pane, cx| {
4785//             pane.activate_item(1, true, true, cx);
4786//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4787//             let item1_id = item1.id();
4788//             let item3_id = item3.id();
4789//             let item4_id = item4.id();
4790//             pane.close_items(cx, SaveIntent::Close, move |id| {
4791//                 [item1_id, item3_id, item4_id].contains(&id)
4792//             })
4793//         });
4794//         cx.foreground().run_until_parked();
4795
4796//         assert!(window.has_pending_prompt(cx));
4797//         // Ignore "Save all" prompt
4798//         window.simulate_prompt_answer(2, cx);
4799//         cx.foreground().run_until_parked();
4800//         // There's a prompt to save item 1.
4801//         pane.read_with(cx, |pane, _| {
4802//             assert_eq!(pane.items_len(), 4);
4803//             assert_eq!(pane.active_item().unwrap().id(), item1.id());
4804//         });
4805//         // Confirm saving item 1.
4806//         window.simulate_prompt_answer(0, cx);
4807//         cx.foreground().run_until_parked();
4808
4809//         // Item 1 is saved. There's a prompt to save item 3.
4810//         pane.read_with(cx, |pane, cx| {
4811//             assert_eq!(item1.read(cx).save_count, 1);
4812//             assert_eq!(item1.read(cx).save_as_count, 0);
4813//             assert_eq!(item1.read(cx).reload_count, 0);
4814//             assert_eq!(pane.items_len(), 3);
4815//             assert_eq!(pane.active_item().unwrap().id(), item3.id());
4816//         });
4817//         assert!(window.has_pending_prompt(cx));
4818
4819//         // Cancel saving item 3.
4820//         window.simulate_prompt_answer(1, cx);
4821//         cx.foreground().run_until_parked();
4822
4823//         // Item 3 is reloaded. There's a prompt to save item 4.
4824//         pane.read_with(cx, |pane, cx| {
4825//             assert_eq!(item3.read(cx).save_count, 0);
4826//             assert_eq!(item3.read(cx).save_as_count, 0);
4827//             assert_eq!(item3.read(cx).reload_count, 1);
4828//             assert_eq!(pane.items_len(), 2);
4829//             assert_eq!(pane.active_item().unwrap().id(), item4.id());
4830//         });
4831//         assert!(window.has_pending_prompt(cx));
4832
4833//         // Confirm saving item 4.
4834//         window.simulate_prompt_answer(0, cx);
4835//         cx.foreground().run_until_parked();
4836
4837//         // There's a prompt for a path for item 4.
4838//         cx.simulate_new_path_selection(|_| Some(Default::default()));
4839//         close_items.await.unwrap();
4840
4841//         // The requested items are closed.
4842//         pane.read_with(cx, |pane, cx| {
4843//             assert_eq!(item4.read(cx).save_count, 0);
4844//             assert_eq!(item4.read(cx).save_as_count, 1);
4845//             assert_eq!(item4.read(cx).reload_count, 0);
4846//             assert_eq!(pane.items_len(), 1);
4847//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4848//         });
4849//     }
4850
4851//     #[gpui::test]
4852//     async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) {
4853//         init_test(cx);
4854
4855//         let fs = FakeFs::new(cx.background());
4856
4857//         let project = Project::test(fs, [], cx).await;
4858//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4859//         let workspace = window.root(cx);
4860
4861//         // Create several workspace items with single project entries, and two
4862//         // workspace items with multiple project entries.
4863//         let single_entry_items = (0..=4)
4864//             .map(|project_entry_id| {
4865//                 window.build_view(cx, |cx| {
4866//                     TestItem::new()
4867//                         .with_dirty(true)
4868//                         .with_project_items(&[TestProjectItem::new(
4869//                             project_entry_id,
4870//                             &format!("{project_entry_id}.txt"),
4871//                             cx,
4872//                         )])
4873//                 })
4874//             })
4875//             .collect::<Vec<_>>();
4876//         let item_2_3 = window.build_view(cx, |cx| {
4877//             TestItem::new()
4878//                 .with_dirty(true)
4879//                 .with_singleton(false)
4880//                 .with_project_items(&[
4881//                     single_entry_items[2].read(cx).project_items[0].clone(),
4882//                     single_entry_items[3].read(cx).project_items[0].clone(),
4883//                 ])
4884//         });
4885//         let item_3_4 = window.build_view(cx, |cx| {
4886//             TestItem::new()
4887//                 .with_dirty(true)
4888//                 .with_singleton(false)
4889//                 .with_project_items(&[
4890//                     single_entry_items[3].read(cx).project_items[0].clone(),
4891//                     single_entry_items[4].read(cx).project_items[0].clone(),
4892//                 ])
4893//         });
4894
4895//         // Create two panes that contain the following project entries:
4896//         //   left pane:
4897//         //     multi-entry items:   (2, 3)
4898//         //     single-entry items:  0, 1, 2, 3, 4
4899//         //   right pane:
4900//         //     single-entry items:  1
4901//         //     multi-entry items:   (3, 4)
4902//         let left_pane = workspace.update(cx, |workspace, cx| {
4903//             let left_pane = workspace.active_pane().clone();
4904//             workspace.add_item(Box::new(item_2_3.clone()), cx);
4905//             for item in single_entry_items {
4906//                 workspace.add_item(Box::new(item), cx);
4907//             }
4908//             left_pane.update(cx, |pane, cx| {
4909//                 pane.activate_item(2, true, true, cx);
4910//             });
4911
4912//             workspace
4913//                 .split_and_clone(left_pane.clone(), SplitDirection::Right, cx)
4914//                 .unwrap();
4915
4916//             left_pane
4917//         });
4918
4919//         //Need to cause an effect flush in order to respect new focus
4920//         workspace.update(cx, |workspace, cx| {
4921//             workspace.add_item(Box::new(item_3_4.clone()), cx);
4922//             cx.focus(&left_pane);
4923//         });
4924
4925//         // When closing all of the items in the left pane, we should be prompted twice:
4926//         // once for project entry 0, and once for project entry 2. After those two
4927//         // prompts, the task should complete.
4928
4929//         let close = left_pane.update(cx, |pane, cx| {
4930//             pane.close_items(cx, SaveIntent::Close, move |_| true)
4931//         });
4932//         cx.foreground().run_until_parked();
4933//         // Discard "Save all" prompt
4934//         window.simulate_prompt_answer(2, cx);
4935
4936//         cx.foreground().run_until_parked();
4937//         left_pane.read_with(cx, |pane, cx| {
4938//             assert_eq!(
4939//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
4940//                 &[ProjectEntryId::from_proto(0)]
4941//             );
4942//         });
4943//         window.simulate_prompt_answer(0, cx);
4944
4945//         cx.foreground().run_until_parked();
4946//         left_pane.read_with(cx, |pane, cx| {
4947//             assert_eq!(
4948//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
4949//                 &[ProjectEntryId::from_proto(2)]
4950//             );
4951//         });
4952//         window.simulate_prompt_answer(0, cx);
4953
4954//         cx.foreground().run_until_parked();
4955//         close.await.unwrap();
4956//         left_pane.read_with(cx, |pane, _| {
4957//             assert_eq!(pane.items_len(), 0);
4958//         });
4959//     }
4960
4961//     #[gpui::test]
4962//     async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
4963//         init_test(cx);
4964
4965//         let fs = FakeFs::new(cx.background());
4966
4967//         let project = Project::test(fs, [], cx).await;
4968//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4969//         let workspace = window.root(cx);
4970//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4971
4972//         let item = window.build_view(cx, |cx| {
4973//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4974//         });
4975//         let item_id = item.id();
4976//         workspace.update(cx, |workspace, cx| {
4977//             workspace.add_item(Box::new(item.clone()), cx);
4978//         });
4979
4980//         // Autosave on window change.
4981//         item.update(cx, |item, cx| {
4982//             cx.update_global(|settings: &mut SettingsStore, cx| {
4983//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
4984//                     settings.autosave = Some(AutosaveSetting::OnWindowChange);
4985//                 })
4986//             });
4987//             item.is_dirty = true;
4988//         });
4989
4990//         // Deactivating the window saves the file.
4991//         window.simulate_deactivation(cx);
4992//         deterministic.run_until_parked();
4993//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
4994
4995//         // Autosave on focus change.
4996//         item.update(cx, |item, cx| {
4997//             cx.focus_self();
4998//             cx.update_global(|settings: &mut SettingsStore, cx| {
4999//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5000//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5001//                 })
5002//             });
5003//             item.is_dirty = true;
5004//         });
5005
5006//         // Blurring the item saves the file.
5007//         item.update(cx, |_, cx| cx.blur());
5008//         deterministic.run_until_parked();
5009//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
5010
5011//         // Deactivating the window still saves the file.
5012//         window.simulate_activation(cx);
5013//         item.update(cx, |item, cx| {
5014//             cx.focus_self();
5015//             item.is_dirty = true;
5016//         });
5017//         window.simulate_deactivation(cx);
5018
5019//         deterministic.run_until_parked();
5020//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5021
5022//         // Autosave after delay.
5023//         item.update(cx, |item, cx| {
5024//             cx.update_global(|settings: &mut SettingsStore, cx| {
5025//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5026//                     settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
5027//                 })
5028//             });
5029//             item.is_dirty = true;
5030//             cx.emit(TestItemEvent::Edit);
5031//         });
5032
5033//         // Delay hasn't fully expired, so the file is still dirty and unsaved.
5034//         deterministic.advance_clock(Duration::from_millis(250));
5035//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5036
5037//         // After delay expires, the file is saved.
5038//         deterministic.advance_clock(Duration::from_millis(250));
5039//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 4));
5040
5041//         // Autosave on focus change, ensuring closing the tab counts as such.
5042//         item.update(cx, |item, cx| {
5043//             cx.update_global(|settings: &mut SettingsStore, cx| {
5044//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5045//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5046//                 })
5047//             });
5048//             item.is_dirty = true;
5049//         });
5050
5051//         pane.update(cx, |pane, cx| {
5052//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5053//         })
5054//         .await
5055//         .unwrap();
5056//         assert!(!window.has_pending_prompt(cx));
5057//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5058
5059//         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
5060//         workspace.update(cx, |workspace, cx| {
5061//             workspace.add_item(Box::new(item.clone()), cx);
5062//         });
5063//         item.update(cx, |item, cx| {
5064//             item.project_items[0].update(cx, |item, _| {
5065//                 item.entry_id = None;
5066//             });
5067//             item.is_dirty = true;
5068//             cx.blur();
5069//         });
5070//         deterministic.run_until_parked();
5071//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5072
5073//         // Ensure autosave is prevented for deleted files also when closing the buffer.
5074//         let _close_items = pane.update(cx, |pane, cx| {
5075//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5076//         });
5077//         deterministic.run_until_parked();
5078//         assert!(window.has_pending_prompt(cx));
5079//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5080//     }
5081
5082//     #[gpui::test]
5083//     async fn test_pane_navigation(cx: &mut gpui::TestAppContext) {
5084//         init_test(cx);
5085
5086//         let fs = FakeFs::new(cx.background());
5087
5088//         let project = Project::test(fs, [], cx).await;
5089//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5090//         let workspace = window.root(cx);
5091
5092//         let item = window.build_view(cx, |cx| {
5093//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
5094//         });
5095//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5096//         let toolbar = pane.read_with(cx, |pane, _| pane.toolbar().clone());
5097//         let toolbar_notify_count = Rc::new(RefCell::new(0));
5098
5099//         workspace.update(cx, |workspace, cx| {
5100//             workspace.add_item(Box::new(item.clone()), cx);
5101//             let toolbar_notification_count = toolbar_notify_count.clone();
5102//             cx.observe(&toolbar, move |_, _, _| {
5103//                 *toolbar_notification_count.borrow_mut() += 1
5104//             })
5105//             .detach();
5106//         });
5107
5108//         pane.read_with(cx, |pane, _| {
5109//             assert!(!pane.can_navigate_backward());
5110//             assert!(!pane.can_navigate_forward());
5111//         });
5112
5113//         item.update(cx, |item, cx| {
5114//             item.set_state("one".to_string(), cx);
5115//         });
5116
5117//         // Toolbar must be notified to re-render the navigation buttons
5118//         assert_eq!(*toolbar_notify_count.borrow(), 1);
5119
5120//         pane.read_with(cx, |pane, _| {
5121//             assert!(pane.can_navigate_backward());
5122//             assert!(!pane.can_navigate_forward());
5123//         });
5124
5125//         workspace
5126//             .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
5127//             .await
5128//             .unwrap();
5129
5130//         assert_eq!(*toolbar_notify_count.borrow(), 3);
5131//         pane.read_with(cx, |pane, _| {
5132//             assert!(!pane.can_navigate_backward());
5133//             assert!(pane.can_navigate_forward());
5134//         });
5135//     }
5136
5137//     #[gpui::test]
5138//     async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) {
5139//         init_test(cx);
5140//         let fs = FakeFs::new(cx.background());
5141
5142//         let project = Project::test(fs, [], cx).await;
5143//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5144//         let workspace = window.root(cx);
5145
5146//         let panel = workspace.update(cx, |workspace, cx| {
5147//             let panel = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5148//             workspace.add_panel(panel.clone(), cx);
5149
5150//             workspace
5151//                 .right_dock()
5152//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5153
5154//             panel
5155//         });
5156
5157//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5158//         pane.update(cx, |pane, cx| {
5159//             let item = cx.build_view(|_| TestItem::new());
5160//             pane.add_item(Box::new(item), true, true, None, cx);
5161//         });
5162
5163//         // Transfer focus from center to panel
5164//         workspace.update(cx, |workspace, cx| {
5165//             workspace.toggle_panel_focus::<TestPanel>(cx);
5166//         });
5167
5168//         workspace.read_with(cx, |workspace, cx| {
5169//             assert!(workspace.right_dock().read(cx).is_open());
5170//             assert!(!panel.is_zoomed(cx));
5171//             assert!(panel.has_focus(cx));
5172//         });
5173
5174//         // Transfer focus from panel to center
5175//         workspace.update(cx, |workspace, cx| {
5176//             workspace.toggle_panel_focus::<TestPanel>(cx);
5177//         });
5178
5179//         workspace.read_with(cx, |workspace, cx| {
5180//             assert!(workspace.right_dock().read(cx).is_open());
5181//             assert!(!panel.is_zoomed(cx));
5182//             assert!(!panel.has_focus(cx));
5183//         });
5184
5185//         // Close the dock
5186//         workspace.update(cx, |workspace, cx| {
5187//             workspace.toggle_dock(DockPosition::Right, cx);
5188//         });
5189
5190//         workspace.read_with(cx, |workspace, cx| {
5191//             assert!(!workspace.right_dock().read(cx).is_open());
5192//             assert!(!panel.is_zoomed(cx));
5193//             assert!(!panel.has_focus(cx));
5194//         });
5195
5196//         // Open the dock
5197//         workspace.update(cx, |workspace, cx| {
5198//             workspace.toggle_dock(DockPosition::Right, cx);
5199//         });
5200
5201//         workspace.read_with(cx, |workspace, cx| {
5202//             assert!(workspace.right_dock().read(cx).is_open());
5203//             assert!(!panel.is_zoomed(cx));
5204//             assert!(panel.has_focus(cx));
5205//         });
5206
5207//         // Focus and zoom panel
5208//         panel.update(cx, |panel, cx| {
5209//             cx.focus_self();
5210//             panel.set_zoomed(true, cx)
5211//         });
5212
5213//         workspace.read_with(cx, |workspace, cx| {
5214//             assert!(workspace.right_dock().read(cx).is_open());
5215//             assert!(panel.is_zoomed(cx));
5216//             assert!(panel.has_focus(cx));
5217//         });
5218
5219//         // Transfer focus to the center closes the dock
5220//         workspace.update(cx, |workspace, cx| {
5221//             workspace.toggle_panel_focus::<TestPanel>(cx);
5222//         });
5223
5224//         workspace.read_with(cx, |workspace, cx| {
5225//             assert!(!workspace.right_dock().read(cx).is_open());
5226//             assert!(panel.is_zoomed(cx));
5227//             assert!(!panel.has_focus(cx));
5228//         });
5229
5230//         // Transferring focus back to the panel keeps it zoomed
5231//         workspace.update(cx, |workspace, cx| {
5232//             workspace.toggle_panel_focus::<TestPanel>(cx);
5233//         });
5234
5235//         workspace.read_with(cx, |workspace, cx| {
5236//             assert!(workspace.right_dock().read(cx).is_open());
5237//             assert!(panel.is_zoomed(cx));
5238//             assert!(panel.has_focus(cx));
5239//         });
5240
5241//         // Close the dock while it is zoomed
5242//         workspace.update(cx, |workspace, cx| {
5243//             workspace.toggle_dock(DockPosition::Right, cx)
5244//         });
5245
5246//         workspace.read_with(cx, |workspace, cx| {
5247//             assert!(!workspace.right_dock().read(cx).is_open());
5248//             assert!(panel.is_zoomed(cx));
5249//             assert!(workspace.zoomed.is_none());
5250//             assert!(!panel.has_focus(cx));
5251//         });
5252
5253//         // Opening the dock, when it's zoomed, retains focus
5254//         workspace.update(cx, |workspace, cx| {
5255//             workspace.toggle_dock(DockPosition::Right, cx)
5256//         });
5257
5258//         workspace.read_with(cx, |workspace, cx| {
5259//             assert!(workspace.right_dock().read(cx).is_open());
5260//             assert!(panel.is_zoomed(cx));
5261//             assert!(workspace.zoomed.is_some());
5262//             assert!(panel.has_focus(cx));
5263//         });
5264
5265//         // Unzoom and close the panel, zoom the active pane.
5266//         panel.update(cx, |panel, cx| panel.set_zoomed(false, cx));
5267//         workspace.update(cx, |workspace, cx| {
5268//             workspace.toggle_dock(DockPosition::Right, cx)
5269//         });
5270//         pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx));
5271
5272//         // Opening a dock unzooms the pane.
5273//         workspace.update(cx, |workspace, cx| {
5274//             workspace.toggle_dock(DockPosition::Right, cx)
5275//         });
5276//         workspace.read_with(cx, |workspace, cx| {
5277//             let pane = pane.read(cx);
5278//             assert!(!pane.is_zoomed());
5279//             assert!(!pane.has_focus());
5280//             assert!(workspace.right_dock().read(cx).is_open());
5281//             assert!(workspace.zoomed.is_none());
5282//         });
5283//     }
5284
5285//     #[gpui::test]
5286//     async fn test_panels(cx: &mut gpui::TestAppContext) {
5287//         init_test(cx);
5288//         let fs = FakeFs::new(cx.background());
5289
5290//         let project = Project::test(fs, [], cx).await;
5291//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5292//         let workspace = window.root(cx);
5293
5294//         let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| {
5295//             // Add panel_1 on the left, panel_2 on the right.
5296//             let panel_1 = cx.build_view(|_| TestPanel::new(DockPosition::Left));
5297//             workspace.add_panel(panel_1.clone(), cx);
5298//             workspace
5299//                 .left_dock()
5300//                 .update(cx, |left_dock, cx| left_dock.set_open(true, cx));
5301//             let panel_2 = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5302//             workspace.add_panel(panel_2.clone(), cx);
5303//             workspace
5304//                 .right_dock()
5305//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5306
5307//             let left_dock = workspace.left_dock();
5308//             assert_eq!(
5309//                 left_dock.read(cx).visible_panel().unwrap().id(),
5310//                 panel_1.id()
5311//             );
5312//             assert_eq!(
5313//                 left_dock.read(cx).active_panel_size(cx).unwrap(),
5314//                 panel_1.size(cx)
5315//             );
5316
5317//             left_dock.update(cx, |left_dock, cx| {
5318//                 left_dock.resize_active_panel(Some(1337.), cx)
5319//             });
5320//             assert_eq!(
5321//                 workspace
5322//                     .right_dock()
5323//                     .read(cx)
5324//                     .visible_panel()
5325//                     .unwrap()
5326//                     .id(),
5327//                 panel_2.id()
5328//             );
5329
5330//             (panel_1, panel_2)
5331//         });
5332
5333//         // Move panel_1 to the right
5334//         panel_1.update(cx, |panel_1, cx| {
5335//             panel_1.set_position(DockPosition::Right, cx)
5336//         });
5337
5338//         workspace.update(cx, |workspace, cx| {
5339//             // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right.
5340//             // Since it was the only panel on the left, the left dock should now be closed.
5341//             assert!(!workspace.left_dock().read(cx).is_open());
5342//             assert!(workspace.left_dock().read(cx).visible_panel().is_none());
5343//             let right_dock = workspace.right_dock();
5344//             assert_eq!(
5345//                 right_dock.read(cx).visible_panel().unwrap().id(),
5346//                 panel_1.id()
5347//             );
5348//             assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5349
5350//             // Now we move panel_2Β to the left
5351//             panel_2.set_position(DockPosition::Left, cx);
5352//         });
5353
5354//         workspace.update(cx, |workspace, cx| {
5355//             // Since panel_2 was not visible on the right, we don't open the left dock.
5356//             assert!(!workspace.left_dock().read(cx).is_open());
5357//             // And the right dock is unaffected in it's displaying of panel_1
5358//             assert!(workspace.right_dock().read(cx).is_open());
5359//             assert_eq!(
5360//                 workspace
5361//                     .right_dock()
5362//                     .read(cx)
5363//                     .visible_panel()
5364//                     .unwrap()
5365//                     .id(),
5366//                 panel_1.id()
5367//             );
5368//         });
5369
5370//         // Move panel_1 back to the left
5371//         panel_1.update(cx, |panel_1, cx| {
5372//             panel_1.set_position(DockPosition::Left, cx)
5373//         });
5374
5375//         workspace.update(cx, |workspace, cx| {
5376//             // Since panel_1 was visible on the right, we open the left dock and make panel_1 active.
5377//             let left_dock = workspace.left_dock();
5378//             assert!(left_dock.read(cx).is_open());
5379//             assert_eq!(
5380//                 left_dock.read(cx).visible_panel().unwrap().id(),
5381//                 panel_1.id()
5382//             );
5383//             assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5384//             // And right the dock should be closed as it no longer has any panels.
5385//             assert!(!workspace.right_dock().read(cx).is_open());
5386
5387//             // Now we move panel_1 to the bottom
5388//             panel_1.set_position(DockPosition::Bottom, cx);
5389//         });
5390
5391//         workspace.update(cx, |workspace, cx| {
5392//             // Since panel_1 was visible on the left, we close the left dock.
5393//             assert!(!workspace.left_dock().read(cx).is_open());
5394//             // The bottom dock is sized based on the panel's default size,
5395//             // since the panel orientation changed from vertical to horizontal.
5396//             let bottom_dock = workspace.bottom_dock();
5397//             assert_eq!(
5398//                 bottom_dock.read(cx).active_panel_size(cx).unwrap(),
5399//                 panel_1.size(cx),
5400//             );
5401//             // Close bottom dock and move panel_1 back to the left.
5402//             bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx));
5403//             panel_1.set_position(DockPosition::Left, cx);
5404//         });
5405
5406//         // Emit activated event on panel 1
5407//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Activated));
5408
5409//         // Now the left dock is open and panel_1 is active and focused.
5410//         workspace.read_with(cx, |workspace, cx| {
5411//             let left_dock = workspace.left_dock();
5412//             assert!(left_dock.read(cx).is_open());
5413//             assert_eq!(
5414//                 left_dock.read(cx).visible_panel().unwrap().id(),
5415//                 panel_1.id()
5416//             );
5417//             assert!(panel_1.is_focused(cx));
5418//         });
5419
5420//         // Emit closed event on panel 2, which is not active
5421//         panel_2.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5422
5423//         // Wo don't close the left dock, because panel_2 wasn't the active panel
5424//         workspace.read_with(cx, |workspace, cx| {
5425//             let left_dock = workspace.left_dock();
5426//             assert!(left_dock.read(cx).is_open());
5427//             assert_eq!(
5428//                 left_dock.read(cx).visible_panel().unwrap().id(),
5429//                 panel_1.id()
5430//             );
5431//         });
5432
5433//         // Emitting a ZoomIn event shows the panel as zoomed.
5434//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn));
5435//         workspace.read_with(cx, |workspace, _| {
5436//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5437//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Left));
5438//         });
5439
5440//         // Move panel to another dock while it is zoomed
5441//         panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx));
5442//         workspace.read_with(cx, |workspace, _| {
5443//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5444//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5445//         });
5446
5447//         // If focus is transferred to another view that's not a panel or another pane, we still show
5448//         // the panel as zoomed.
5449//         let focus_receiver = window.build_view(cx, |_| EmptyView);
5450//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5451//         workspace.read_with(cx, |workspace, _| {
5452//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5453//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5454//         });
5455
5456//         // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed.
5457//         workspace.update(cx, |_, cx| cx.focus_self());
5458//         workspace.read_with(cx, |workspace, _| {
5459//             assert_eq!(workspace.zoomed, None);
5460//             assert_eq!(workspace.zoomed_position, None);
5461//         });
5462
5463//         // If focus is transferred again to another view that's not a panel or a pane, we won't
5464//         // show the panel as zoomed because it wasn't zoomed before.
5465//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5466//         workspace.read_with(cx, |workspace, _| {
5467//             assert_eq!(workspace.zoomed, None);
5468//             assert_eq!(workspace.zoomed_position, None);
5469//         });
5470
5471//         // When focus is transferred back to the panel, it is zoomed again.
5472//         panel_1.update(cx, |_, cx| cx.focus_self());
5473//         workspace.read_with(cx, |workspace, _| {
5474//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5475//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5476//         });
5477
5478//         // Emitting a ZoomOut event unzooms the panel.
5479//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut));
5480//         workspace.read_with(cx, |workspace, _| {
5481//             assert_eq!(workspace.zoomed, None);
5482//             assert_eq!(workspace.zoomed_position, None);
5483//         });
5484
5485//         // Emit closed event on panel 1, which is active
5486//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5487
5488//         // Now the left dock is closed, because panel_1 was the active panel
5489//         workspace.read_with(cx, |workspace, cx| {
5490//             let right_dock = workspace.right_dock();
5491//             assert!(!right_dock.read(cx).is_open());
5492//         });
5493//     }
5494
5495//     pub fn init_test(cx: &mut TestAppContext) {
5496//         cx.foreground().forbid_parking();
5497//         cx.update(|cx| {
5498//             cx.set_global(SettingsStore::test(cx));
5499//             theme::init((), cx);
5500//             language::init(cx);
5501//             crate::init_settings(cx);
5502//             Project::init_settings(cx);
5503//         });
5504//     }
5505// }