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