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