workspace2.rs

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