workspace2.rs

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