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