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