workspace2.rs

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