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        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
3618
3619        self.add_workspace_actions_listeners(div())
3620            .key_context(context)
3621            .relative()
3622            .size_full()
3623            .flex()
3624            .flex_col()
3625            .font(ui_font)
3626            .gap_0()
3627            .justify_start()
3628            .items_start()
3629            .text_color(cx.theme().colors().text)
3630            .bg(cx.theme().colors().background)
3631            .children(self.titlebar_item.clone())
3632            .child(
3633                // todo! should this be a component a view?
3634                div()
3635                    .id("workspace")
3636                    .relative()
3637                    .flex_1()
3638                    .w_full()
3639                    .flex()
3640                    .overflow_hidden()
3641                    .border_t()
3642                    .border_b()
3643                    .border_color(cx.theme().colors().border)
3644                    .child(self.modal_layer.clone())
3645                    .child(
3646                        div()
3647                            .flex()
3648                            .flex_row()
3649                            .flex_1()
3650                            .h_full()
3651                            // Left Dock
3652                            .child(
3653                                div()
3654                                    .flex()
3655                                    .flex_none()
3656                                    .overflow_hidden()
3657                                    .child(self.left_dock.clone()),
3658                            )
3659                            // Panes
3660                            .child(
3661                                div()
3662                                    .flex()
3663                                    .flex_col()
3664                                    .flex_1()
3665                                    .child(self.center.render(
3666                                        &self.project,
3667                                        &self.follower_states,
3668                                        self.active_call(),
3669                                        &self.active_pane,
3670                                        self.zoomed.as_ref(),
3671                                        &self.app_state,
3672                                        cx,
3673                                    ))
3674                                    .child(div().flex().flex_1().child(self.bottom_dock.clone())),
3675                            )
3676                            // Right Dock
3677                            .child(
3678                                div()
3679                                    .flex()
3680                                    .flex_none()
3681                                    .overflow_hidden()
3682                                    .child(self.right_dock.clone()),
3683                            ),
3684                    ),
3685            )
3686            .child(self.status_bar.clone())
3687            // .when(self.debug.show_toast, |this| {
3688            //     this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
3689            // })
3690            // .children(
3691            //     Some(
3692            //         div()
3693            //             .absolute()
3694            //             .top(px(50.))
3695            //             .left(px(640.))
3696            //             .z_index(8)
3697            //             .child(LanguageSelector::new("language-selector")),
3698            //     )
3699            //     .filter(|_| self.is_language_selector_open()),
3700            // )
3701            .z_index(8)
3702            // Debug
3703            .child(
3704                div()
3705                    .flex()
3706                    .flex_col()
3707                    .z_index(9)
3708                    .absolute()
3709                    .top_20()
3710                    .left_1_4()
3711                    .w_40()
3712                    .gap_2(), // .when(self.show_debug, |this| {
3713                              //     this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
3714                              //         Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
3715                              //     ))
3716                              //     .child(
3717                              //         Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
3718                              //             |workspace, cx| workspace.debug_toggle_toast(cx),
3719                              //         )),
3720                              //     )
3721                              //     .child(
3722                              //         Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
3723                              //             |workspace, cx| workspace.debug_toggle_livestream(cx),
3724                              //         )),
3725                              //     )
3726                              // })
3727                              // .child(
3728                              //     Button::<Workspace>::new("Toggle Debug")
3729                              //         .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
3730                              // ),
3731            )
3732    }
3733}
3734// todo!()
3735// impl Entity for Workspace {
3736//     type Event = Event;
3737
3738//     fn release(&mut self, cx: &mut AppContext) {
3739//         self.app_state.workspace_store.update(cx, |store, _| {
3740//             store.workspaces.remove(&self.weak_self);
3741//         })
3742//     }
3743// }
3744
3745// impl View for Workspace {
3746//     fn ui_name() -> &'static str {
3747//         "Workspace"
3748//     }
3749
3750//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
3751//         let theme = theme::current(cx).clone();
3752//         Stack::new()
3753//             .with_child(
3754//                 Flex::column()
3755//                     .with_child(self.render_titlebar(&theme, cx))
3756//                     .with_child(
3757//                         Stack::new()
3758//                             .with_child({
3759//                                 let project = self.project.clone();
3760//                                 Flex::row()
3761//                                     .with_children(self.render_dock(DockPosition::Left, cx))
3762//                                     .with_child(
3763//                                         Flex::column()
3764//                                             .with_child(
3765//                                                 FlexItem::new(
3766//                                                     self.center.render(
3767//                                                         &project,
3768//                                                         &theme,
3769//                                                         &self.follower_states,
3770//                                                         self.active_call(),
3771//                                                         self.active_pane(),
3772//                                                         self.zoomed
3773//                                                             .as_ref()
3774//                                                             .and_then(|zoomed| zoomed.upgrade(cx))
3775//                                                             .as_ref(),
3776//                                                         &self.app_state,
3777//                                                         cx,
3778//                                                     ),
3779//                                                 )
3780//                                                 .flex(1., true),
3781//                                             )
3782//                                             .with_children(
3783//                                                 self.render_dock(DockPosition::Bottom, cx),
3784//                                             )
3785//                                             .flex(1., true),
3786//                                     )
3787//                                     .with_children(self.render_dock(DockPosition::Right, cx))
3788//                             })
3789//                             .with_child(Overlay::new(
3790//                                 Stack::new()
3791//                                     .with_children(self.zoomed.as_ref().and_then(|zoomed| {
3792//                                         enum ZoomBackground {}
3793//                                         let zoomed = zoomed.upgrade(cx)?;
3794
3795//                                         let mut foreground_style =
3796//                                             theme.workspace.zoomed_pane_foreground;
3797//                                         if let Some(zoomed_dock_position) = self.zoomed_position {
3798//                                             foreground_style =
3799//                                                 theme.workspace.zoomed_panel_foreground;
3800//                                             let margin = foreground_style.margin.top;
3801//                                             let border = foreground_style.border.top;
3802
3803//                                             // Only include a margin and border on the opposite side.
3804//                                             foreground_style.margin.top = 0.;
3805//                                             foreground_style.margin.left = 0.;
3806//                                             foreground_style.margin.bottom = 0.;
3807//                                             foreground_style.margin.right = 0.;
3808//                                             foreground_style.border.top = false;
3809//                                             foreground_style.border.left = false;
3810//                                             foreground_style.border.bottom = false;
3811//                                             foreground_style.border.right = false;
3812//                                             match zoomed_dock_position {
3813//                                                 DockPosition::Left => {
3814//                                                     foreground_style.margin.right = margin;
3815//                                                     foreground_style.border.right = border;
3816//                                                 }
3817//                                                 DockPosition::Right => {
3818//                                                     foreground_style.margin.left = margin;
3819//                                                     foreground_style.border.left = border;
3820//                                                 }
3821//                                                 DockPosition::Bottom => {
3822//                                                     foreground_style.margin.top = margin;
3823//                                                     foreground_style.border.top = border;
3824//                                                 }
3825//                                             }
3826//                                         }
3827
3828//                                         Some(
3829//                                             ChildView::new(&zoomed, cx)
3830//                                                 .contained()
3831//                                                 .with_style(foreground_style)
3832//                                                 .aligned()
3833//                                                 .contained()
3834//                                                 .with_style(theme.workspace.zoomed_background)
3835//                                                 .mouse::<ZoomBackground>(0)
3836//                                                 .capture_all()
3837//                                                 .on_down(
3838//                                                     MouseButton::Left,
3839//                                                     |_, this: &mut Self, cx| {
3840//                                                         this.zoom_out(cx);
3841//                                                     },
3842//                                                 ),
3843//                                         )
3844//                                     }))
3845//                                     .with_children(self.modal.as_ref().map(|modal| {
3846//                                         // Prevent clicks within the modal from falling
3847//                                         // through to the rest of the workspace.
3848//                                         enum ModalBackground {}
3849//                                         MouseEventHandler::new::<ModalBackground, _>(
3850//                                             0,
3851//                                             cx,
3852//                                             |_, cx| ChildView::new(modal.view.as_any(), cx),
3853//                                         )
3854//                                         .on_click(MouseButton::Left, |_, _, _| {})
3855//                                         .contained()
3856//                                         .with_style(theme.workspace.modal)
3857//                                         .aligned()
3858//                                         .top()
3859//                                     }))
3860//                                     .with_children(self.render_notifications(&theme.workspace, cx)),
3861//                             ))
3862//                             .provide_resize_bounds::<WorkspaceBounds>()
3863//                             .flex(1.0, true),
3864//                     )
3865//                     .with_child(ChildView::new(&self.status_bar, cx))
3866//                     .contained()
3867//                     .with_background_color(theme.workspace.background),
3868//             )
3869//             .with_children(DragAndDrop::render(cx))
3870//             .with_children(self.render_disconnected_overlay(cx))
3871//             .into_any_named("workspace")
3872//     }
3873
3874//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
3875//         if cx.is_self_focused() {
3876//             cx.focus(&self.active_pane);
3877//         }
3878//     }
3879
3880//     fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
3881//         DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
3882//     }
3883// }
3884
3885impl WorkspaceStore {
3886    pub fn new(client: Arc<Client>, _cx: &mut ModelContext<Self>) -> Self {
3887        Self {
3888            workspaces: Default::default(),
3889            followers: Default::default(),
3890            _subscriptions: vec![],
3891            //     client.add_request_handler(cx.weak_model(), Self::handle_follow),
3892            //     client.add_message_handler(cx.weak_model(), Self::handle_unfollow),
3893            //     client.add_message_handler(cx.weak_model(), Self::handle_update_followers),
3894            // ],
3895            client,
3896        }
3897    }
3898
3899    pub fn update_followers(
3900        &self,
3901        project_id: Option<u64>,
3902        update: proto::update_followers::Variant,
3903        cx: &AppContext,
3904    ) -> Option<()> {
3905        if !cx.has_global::<Model<ActiveCall>>() {
3906            return None;
3907        }
3908
3909        let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id();
3910        let follower_ids: Vec<_> = self
3911            .followers
3912            .iter()
3913            .filter_map(|follower| {
3914                if follower.project_id == project_id || project_id.is_none() {
3915                    Some(follower.peer_id.into())
3916                } else {
3917                    None
3918                }
3919            })
3920            .collect();
3921        if follower_ids.is_empty() {
3922            return None;
3923        }
3924        self.client
3925            .send(proto::UpdateFollowers {
3926                room_id,
3927                project_id,
3928                follower_ids,
3929                variant: Some(update),
3930            })
3931            .log_err()
3932    }
3933
3934    pub async fn handle_follow(
3935        this: Model<Self>,
3936        envelope: TypedEnvelope<proto::Follow>,
3937        _: Arc<Client>,
3938        mut cx: AsyncAppContext,
3939    ) -> Result<proto::FollowResponse> {
3940        this.update(&mut cx, |this, cx| {
3941            let follower = Follower {
3942                project_id: envelope.payload.project_id,
3943                peer_id: envelope.original_sender_id()?,
3944            };
3945            let active_project = ActiveCall::global(cx).read(cx).location().cloned();
3946
3947            let mut response = proto::FollowResponse::default();
3948            for workspace in &this.workspaces {
3949                workspace
3950                    .update(cx, |workspace, cx| {
3951                        let handler_response = workspace.handle_follow(follower.project_id, cx);
3952                        if response.views.is_empty() {
3953                            response.views = handler_response.views;
3954                        } else {
3955                            response.views.extend_from_slice(&handler_response.views);
3956                        }
3957
3958                        if let Some(active_view_id) = handler_response.active_view_id.clone() {
3959                            if response.active_view_id.is_none()
3960                                || Some(workspace.project.downgrade()) == active_project
3961                            {
3962                                response.active_view_id = Some(active_view_id);
3963                            }
3964                        }
3965                    })
3966                    .ok();
3967            }
3968
3969            if let Err(ix) = this.followers.binary_search(&follower) {
3970                this.followers.insert(ix, follower);
3971            }
3972
3973            Ok(response)
3974        })?
3975    }
3976
3977    async fn handle_unfollow(
3978        model: Model<Self>,
3979        envelope: TypedEnvelope<proto::Unfollow>,
3980        _: Arc<Client>,
3981        mut cx: AsyncAppContext,
3982    ) -> Result<()> {
3983        model.update(&mut cx, |this, _| {
3984            let follower = Follower {
3985                project_id: envelope.payload.project_id,
3986                peer_id: envelope.original_sender_id()?,
3987            };
3988            if let Ok(ix) = this.followers.binary_search(&follower) {
3989                this.followers.remove(ix);
3990            }
3991            Ok(())
3992        })?
3993    }
3994
3995    async fn handle_update_followers(
3996        this: Model<Self>,
3997        envelope: TypedEnvelope<proto::UpdateFollowers>,
3998        _: Arc<Client>,
3999        mut cx: AsyncWindowContext,
4000    ) -> Result<()> {
4001        let leader_id = envelope.original_sender_id()?;
4002        let update = envelope.payload;
4003
4004        this.update(&mut cx, |this, cx| {
4005            for workspace in &this.workspaces {
4006                workspace.update(cx, |workspace, cx| {
4007                    let project_id = workspace.project.read(cx).remote_id();
4008                    if update.project_id != project_id && update.project_id.is_some() {
4009                        return;
4010                    }
4011                    workspace.handle_update_followers(leader_id, update.clone(), cx);
4012                })?;
4013            }
4014            Ok(())
4015        })?
4016    }
4017}
4018
4019impl ViewId {
4020    pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
4021        Ok(Self {
4022            creator: message
4023                .creator
4024                .ok_or_else(|| anyhow!("creator is missing"))?,
4025            id: message.id,
4026        })
4027    }
4028
4029    pub(crate) fn to_proto(&self) -> proto::ViewId {
4030        proto::ViewId {
4031            creator: Some(self.creator),
4032            id: self.id,
4033        }
4034    }
4035}
4036
4037pub trait WorkspaceHandle {
4038    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
4039}
4040
4041impl WorkspaceHandle for View<Workspace> {
4042    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
4043        self.read(cx)
4044            .worktrees(cx)
4045            .flat_map(|worktree| {
4046                let worktree_id = worktree.read(cx).id();
4047                worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
4048                    worktree_id,
4049                    path: f.path.clone(),
4050                })
4051            })
4052            .collect::<Vec<_>>()
4053    }
4054}
4055
4056// impl std::fmt::Debug for OpenPaths {
4057//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4058//         f.debug_struct("OpenPaths")
4059//             .field("paths", &self.paths)
4060//             .finish()
4061//     }
4062// }
4063
4064pub struct WorkspaceCreated(pub WeakView<Workspace>);
4065
4066pub fn activate_workspace_for_project(
4067    cx: &mut AppContext,
4068    predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static,
4069) -> Option<WindowHandle<Workspace>> {
4070    for window in cx.windows() {
4071        let Some(workspace) = window.downcast::<Workspace>() else {
4072            continue;
4073        };
4074
4075        let predicate = workspace
4076            .update(cx, |workspace, cx| {
4077                let project = workspace.project.read(cx);
4078                if predicate(project, cx) {
4079                    cx.activate_window();
4080                    true
4081                } else {
4082                    false
4083                }
4084            })
4085            .log_err()
4086            .unwrap_or(false);
4087
4088        if predicate {
4089            return Some(workspace);
4090        }
4091    }
4092
4093    None
4094}
4095
4096pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
4097    DB.last_workspace().await.log_err().flatten()
4098}
4099
4100// async fn join_channel_internal(
4101//     channel_id: u64,
4102//     app_state: &Arc<AppState>,
4103//     requesting_window: Option<WindowHandle<Workspace>>,
4104//     active_call: &ModelHandle<ActiveCall>,
4105//     cx: &mut AsyncAppContext,
4106// ) -> Result<bool> {
4107//     let (should_prompt, open_room) = active_call.read_with(cx, |active_call, cx| {
4108//         let Some(room) = active_call.room().map(|room| room.read(cx)) else {
4109//             return (false, None);
4110//         };
4111
4112//         let already_in_channel = room.channel_id() == Some(channel_id);
4113//         let should_prompt = room.is_sharing_project()
4114//             && room.remote_participants().len() > 0
4115//             && !already_in_channel;
4116//         let open_room = if already_in_channel {
4117//             active_call.room().cloned()
4118//         } else {
4119//             None
4120//         };
4121//         (should_prompt, open_room)
4122//     });
4123
4124//     if let Some(room) = open_room {
4125//         let task = room.update(cx, |room, cx| {
4126//             if let Some((project, host)) = room.most_active_project(cx) {
4127//                 return Some(join_remote_project(project, host, app_state.clone(), cx));
4128//             }
4129
4130//             None
4131//         });
4132//         if let Some(task) = task {
4133//             task.await?;
4134//         }
4135//         return anyhow::Ok(true);
4136//     }
4137
4138//     if should_prompt {
4139//         if let Some(workspace) = requesting_window {
4140//             if let Some(window) = workspace.update(cx, |cx| cx.window()) {
4141//                 let answer = window.prompt(
4142//                     PromptLevel::Warning,
4143//                     "Leaving this call will unshare your current project.\nDo you want to switch channels?",
4144//                     &["Yes, Join Channel", "Cancel"],
4145//                     cx,
4146//                 );
4147
4148//                 if let Some(mut answer) = answer {
4149//                     if answer.next().await == Some(1) {
4150//                         return Ok(false);
4151//                     }
4152//                 }
4153//             } else {
4154//                 return Ok(false); // unreachable!() hopefully
4155//             }
4156//         } else {
4157//             return Ok(false); // unreachable!() hopefully
4158//         }
4159//     }
4160
4161//     let client = cx.read(|cx| active_call.read(cx).client());
4162
4163//     let mut client_status = client.status();
4164
4165//     // this loop will terminate within client::CONNECTION_TIMEOUT seconds.
4166//     'outer: loop {
4167//         let Some(status) = client_status.recv().await else {
4168//             return Err(anyhow!("error connecting"));
4169//         };
4170
4171//         match status {
4172//             Status::Connecting
4173//             | Status::Authenticating
4174//             | Status::Reconnecting
4175//             | Status::Reauthenticating => continue,
4176//             Status::Connected { .. } => break 'outer,
4177//             Status::SignedOut => return Err(anyhow!("not signed in")),
4178//             Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
4179//             Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
4180//                 return Err(anyhow!("zed is offline"))
4181//             }
4182//         }
4183//     }
4184
4185//     let room = active_call
4186//         .update(cx, |active_call, cx| {
4187//             active_call.join_channel(channel_id, cx)
4188//         })
4189//         .await?;
4190
4191//     room.update(cx, |room, _| room.room_update_completed())
4192//         .await;
4193
4194//     let task = room.update(cx, |room, cx| {
4195//         if let Some((project, host)) = room.most_active_project(cx) {
4196//             return Some(join_remote_project(project, host, app_state.clone(), cx));
4197//         }
4198
4199//         None
4200//     });
4201//     if let Some(task) = task {
4202//         task.await?;
4203//         return anyhow::Ok(true);
4204//     }
4205//     anyhow::Ok(false)
4206// }
4207
4208// pub fn join_channel(
4209//     channel_id: u64,
4210//     app_state: Arc<AppState>,
4211//     requesting_window: Option<WindowHandle<Workspace>>,
4212//     cx: &mut AppContext,
4213// ) -> Task<Result<()>> {
4214//     let active_call = ActiveCall::global(cx);
4215//     cx.spawn(|mut cx| async move {
4216//         let result = join_channel_internal(
4217//             channel_id,
4218//             &app_state,
4219//             requesting_window,
4220//             &active_call,
4221//             &mut cx,
4222//         )
4223//         .await;
4224
4225//         // join channel succeeded, and opened a window
4226//         if matches!(result, Ok(true)) {
4227//             return anyhow::Ok(());
4228//         }
4229
4230//         if requesting_window.is_some() {
4231//             return anyhow::Ok(());
4232//         }
4233
4234//         // find an existing workspace to focus and show call controls
4235//         let mut active_window = activate_any_workspace_window(&mut cx);
4236//         if active_window.is_none() {
4237//             // no open workspaces, make one to show the error in (blergh)
4238//             cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
4239//                 .await;
4240//         }
4241
4242//         active_window = activate_any_workspace_window(&mut cx);
4243//         if active_window.is_none() {
4244//             return result.map(|_| ()); // unreachable!() assuming new_local always opens a window
4245//         }
4246
4247//         if let Err(err) = result {
4248//             let prompt = active_window.unwrap().prompt(
4249//                 PromptLevel::Critical,
4250//                 &format!("Failed to join channel: {}", err),
4251//                 &["Ok"],
4252//                 &mut cx,
4253//             );
4254//             if let Some(mut prompt) = prompt {
4255//                 prompt.next().await;
4256//             } else {
4257//                 return Err(err);
4258//             }
4259//         }
4260
4261//         // return ok, we showed the error to the user.
4262//         return anyhow::Ok(());
4263//     })
4264// }
4265
4266// pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
4267//     for window in cx.windows() {
4268//         let found = window.update(cx, |cx| {
4269//             let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
4270//             if is_workspace {
4271//                 cx.activate_window();
4272//             }
4273//             is_workspace
4274//         });
4275//         if found == Some(true) {
4276//             return Some(window);
4277//         }
4278//     }
4279//     None
4280// }
4281
4282#[allow(clippy::type_complexity)]
4283pub fn open_paths(
4284    abs_paths: &[PathBuf],
4285    app_state: &Arc<AppState>,
4286    requesting_window: Option<WindowHandle<Workspace>>,
4287    cx: &mut AppContext,
4288) -> Task<
4289    anyhow::Result<(
4290        WindowHandle<Workspace>,
4291        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
4292    )>,
4293> {
4294    let app_state = app_state.clone();
4295    let abs_paths = abs_paths.to_vec();
4296    // Open paths in existing workspace if possible
4297    let existing = activate_workspace_for_project(cx, {
4298        let abs_paths = abs_paths.clone();
4299        move |project, cx| project.contains_paths(&abs_paths, cx)
4300    });
4301    cx.spawn(move |mut cx| async move {
4302        if let Some(existing) = existing {
4303            // // Ok((
4304            //     existing.clone(),
4305            //     cx.update_window_root(&existing, |workspace, cx| {
4306            //         workspace.open_paths(abs_paths, true, cx)
4307            //     })?
4308            //     .await,
4309            // ))
4310            todo!()
4311        } else {
4312            cx.update(move |cx| {
4313                Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
4314            })?
4315            .await
4316        }
4317    })
4318}
4319
4320pub fn open_new(
4321    app_state: &Arc<AppState>,
4322    cx: &mut AppContext,
4323    init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static + Send,
4324) -> Task<()> {
4325    let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
4326    cx.spawn(|mut cx| async move {
4327        if let Some((workspace, opened_paths)) = task.await.log_err() {
4328            workspace
4329                .update(&mut cx, |workspace, cx| {
4330                    if opened_paths.is_empty() {
4331                        init(workspace, cx)
4332                    }
4333                })
4334                .log_err();
4335        }
4336    })
4337}
4338
4339pub fn create_and_open_local_file(
4340    path: &'static Path,
4341    cx: &mut ViewContext<Workspace>,
4342    default_content: impl 'static + Send + FnOnce() -> Rope,
4343) -> Task<Result<Box<dyn ItemHandle>>> {
4344    cx.spawn(|workspace, mut cx| async move {
4345        let fs = workspace.update(&mut cx, |workspace, _| workspace.app_state().fs.clone())?;
4346        if !fs.is_file(path).await {
4347            fs.create_file(path, Default::default()).await?;
4348            fs.save(path, &default_content(), Default::default())
4349                .await?;
4350        }
4351
4352        let mut items = workspace
4353            .update(&mut cx, |workspace, cx| {
4354                workspace.with_local_workspace(cx, |workspace, cx| {
4355                    workspace.open_paths(vec![path.to_path_buf()], false, cx)
4356                })
4357            })?
4358            .await?
4359            .await;
4360
4361        let item = items.pop().flatten();
4362        item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
4363    })
4364}
4365
4366// pub fn join_remote_project(
4367//     project_id: u64,
4368//     follow_user_id: u64,
4369//     app_state: Arc<AppState>,
4370//     cx: &mut AppContext,
4371// ) -> Task<Result<()>> {
4372//     cx.spawn(|mut cx| async move {
4373//         let windows = cx.windows();
4374//         let existing_workspace = windows.into_iter().find_map(|window| {
4375//             window.downcast::<Workspace>().and_then(|window| {
4376//                 window
4377//                     .read_root_with(&cx, |workspace, cx| {
4378//                         if workspace.project().read(cx).remote_id() == Some(project_id) {
4379//                             Some(cx.handle().downgrade())
4380//                         } else {
4381//                             None
4382//                         }
4383//                     })
4384//                     .unwrap_or(None)
4385//             })
4386//         });
4387
4388//         let workspace = if let Some(existing_workspace) = existing_workspace {
4389//             existing_workspace
4390//         } else {
4391//             let active_call = cx.read(ActiveCall::global);
4392//             let room = active_call
4393//                 .read_with(&cx, |call, _| call.room().cloned())
4394//                 .ok_or_else(|| anyhow!("not in a call"))?;
4395//             let project = room
4396//                 .update(&mut cx, |room, cx| {
4397//                     room.join_project(
4398//                         project_id,
4399//                         app_state.languages.clone(),
4400//                         app_state.fs.clone(),
4401//                         cx,
4402//                     )
4403//                 })
4404//                 .await?;
4405
4406//             let window_bounds_override = window_bounds_env_override(&cx);
4407//             let window = cx.add_window(
4408//                 (app_state.build_window_options)(
4409//                     window_bounds_override,
4410//                     None,
4411//                     cx.platform().as_ref(),
4412//                 ),
4413//                 |cx| Workspace::new(0, project, app_state.clone(), cx),
4414//             );
4415//             let workspace = window.root(&cx).unwrap();
4416//             (app_state.initialize_workspace)(
4417//                 workspace.downgrade(),
4418//                 false,
4419//                 app_state.clone(),
4420//                 cx.clone(),
4421//             )
4422//             .await
4423//             .log_err();
4424
4425//             workspace.downgrade()
4426//         };
4427
4428//         workspace.window().activate(&mut cx);
4429//         cx.platform().activate(true);
4430
4431//         workspace.update(&mut cx, |workspace, cx| {
4432//             if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
4433//                 let follow_peer_id = room
4434//                     .read(cx)
4435//                     .remote_participants()
4436//                     .iter()
4437//                     .find(|(_, participant)| participant.user.id == follow_user_id)
4438//                     .map(|(_, p)| p.peer_id)
4439//                     .or_else(|| {
4440//                         // If we couldn't follow the given user, follow the host instead.
4441//                         let collaborator = workspace
4442//                             .project()
4443//                             .read(cx)
4444//                             .collaborators()
4445//                             .values()
4446//                             .find(|collaborator| collaborator.replica_id == 0)?;
4447//                         Some(collaborator.peer_id)
4448//                     });
4449
4450//                 if let Some(follow_peer_id) = follow_peer_id {
4451//                     workspace
4452//                         .follow(follow_peer_id, cx)
4453//                         .map(|follow| follow.detach_and_log_err(cx));
4454//                 }
4455//             }
4456//         })?;
4457
4458//         anyhow::Ok(())
4459//     })
4460// }
4461
4462// pub fn restart(_: &Restart, cx: &mut AppContext) {
4463//     let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
4464//     cx.spawn(|mut cx| async move {
4465//         let mut workspace_windows = cx
4466//             .windows()
4467//             .into_iter()
4468//             .filter_map(|window| window.downcast::<Workspace>())
4469//             .collect::<Vec<_>>();
4470
4471//         // If multiple windows have unsaved changes, and need a save prompt,
4472//         // prompt in the active window before switching to a different window.
4473//         workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
4474
4475//         if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
4476//             let answer = window.prompt(
4477//                 PromptLevel::Info,
4478//                 "Are you sure you want to restart?",
4479//                 &["Restart", "Cancel"],
4480//                 &mut cx,
4481//             );
4482
4483//             if let Some(mut answer) = answer {
4484//                 let answer = answer.next().await;
4485//                 if answer != Some(0) {
4486//                     return Ok(());
4487//                 }
4488//             }
4489//         }
4490
4491//         // If the user cancels any save prompt, then keep the app open.
4492//         for window in workspace_windows {
4493//             if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
4494//                 workspace.prepare_to_close(true, cx)
4495//             }) {
4496//                 if !should_close.await? {
4497//                     return Ok(());
4498//                 }
4499//             }
4500//         }
4501//         cx.platform().restart();
4502//         anyhow::Ok(())
4503//     })
4504//     .detach_and_log_err(cx);
4505// }
4506
4507fn parse_pixel_position_env_var(value: &str) -> Option<Point<GlobalPixels>> {
4508    let mut parts = value.split(',');
4509    let x: usize = parts.next()?.parse().ok()?;
4510    let y: usize = parts.next()?.parse().ok()?;
4511    Some(point((x as f64).into(), (y as f64).into()))
4512}
4513
4514fn parse_pixel_size_env_var(value: &str) -> Option<Size<GlobalPixels>> {
4515    let mut parts = value.split(',');
4516    let width: usize = parts.next()?.parse().ok()?;
4517    let height: usize = parts.next()?.parse().ok()?;
4518    Some(size((width as f64).into(), (height as f64).into()))
4519}
4520
4521// #[cfg(test)]
4522// mod tests {
4523//     use super::*;
4524//     use crate::{
4525//         dock::test::{TestPanel, TestPanelEvent},
4526//         item::test::{TestItem, TestItemEvent, TestProjectItem},
4527//     };
4528//     use fs::FakeFs;
4529//     use gpui::{executor::Deterministic, test::EmptyView, TestAppContext};
4530//     use project::{Project, ProjectEntryId};
4531//     use serde_json::json;
4532//     use settings::SettingsStore;
4533//     use std::{cell::RefCell, rc::Rc};
4534
4535//     #[gpui::test]
4536//     async fn test_tab_disambiguation(cx: &mut TestAppContext) {
4537//         init_test(cx);
4538
4539//         let fs = FakeFs::new(cx.background());
4540//         let project = Project::test(fs, [], cx).await;
4541//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4542//         let workspace = window.root(cx);
4543
4544//         // Adding an item with no ambiguity renders the tab without detail.
4545//         let item1 = window.build_view(cx, |_| {
4546//             let mut item = TestItem::new();
4547//             item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
4548//             item
4549//         });
4550//         workspace.update(cx, |workspace, cx| {
4551//             workspace.add_item(Box::new(item1.clone()), cx);
4552//         });
4553//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None));
4554
4555//         // Adding an item that creates ambiguity increases the level of detail on
4556//         // both tabs.
4557//         let item2 = window.build_view(cx, |_| {
4558//             let mut item = TestItem::new();
4559//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4560//             item
4561//         });
4562//         workspace.update(cx, |workspace, cx| {
4563//             workspace.add_item(Box::new(item2.clone()), cx);
4564//         });
4565//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4566//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4567
4568//         // Adding an item that creates ambiguity increases the level of detail only
4569//         // on the ambiguous tabs. In this case, the ambiguity can't be resolved so
4570//         // we stop at the highest detail available.
4571//         let item3 = window.build_view(cx, |_| {
4572//             let mut item = TestItem::new();
4573//             item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
4574//             item
4575//         });
4576//         workspace.update(cx, |workspace, cx| {
4577//             workspace.add_item(Box::new(item3.clone()), cx);
4578//         });
4579//         item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
4580//         item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4581//         item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
4582//     }
4583
4584//     #[gpui::test]
4585//     async fn test_tracking_active_path(cx: &mut TestAppContext) {
4586//         init_test(cx);
4587
4588//         let fs = FakeFs::new(cx.background());
4589//         fs.insert_tree(
4590//             "/root1",
4591//             json!({
4592//                 "one.txt": "",
4593//                 "two.txt": "",
4594//             }),
4595//         )
4596//         .await;
4597//         fs.insert_tree(
4598//             "/root2",
4599//             json!({
4600//                 "three.txt": "",
4601//             }),
4602//         )
4603//         .await;
4604
4605//         let project = Project::test(fs, ["root1".as_ref()], cx).await;
4606//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4607//         let workspace = window.root(cx);
4608//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4609//         let worktree_id = project.read_with(cx, |project, cx| {
4610//             project.worktrees(cx).next().unwrap().read(cx).id()
4611//         });
4612
4613//         let item1 = window.build_view(cx, |cx| {
4614//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
4615//         });
4616//         let item2 = window.build_view(cx, |cx| {
4617//             TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
4618//         });
4619
4620//         // Add an item to an empty pane
4621//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx));
4622//         project.read_with(cx, |project, cx| {
4623//             assert_eq!(
4624//                 project.active_entry(),
4625//                 project
4626//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4627//                     .map(|e| e.id)
4628//             );
4629//         });
4630//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4631
4632//         // Add a second item to a non-empty pane
4633//         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
4634//         assert_eq!(window.current_title(cx).as_deref(), Some("two.txt β€” root1"));
4635//         project.read_with(cx, |project, cx| {
4636//             assert_eq!(
4637//                 project.active_entry(),
4638//                 project
4639//                     .entry_for_path(&(worktree_id, "two.txt").into(), cx)
4640//                     .map(|e| e.id)
4641//             );
4642//         });
4643
4644//         // Close the active item
4645//         pane.update(cx, |pane, cx| {
4646//             pane.close_active_item(&Default::default(), cx).unwrap()
4647//         })
4648//         .await
4649//         .unwrap();
4650//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
4651//         project.read_with(cx, |project, cx| {
4652//             assert_eq!(
4653//                 project.active_entry(),
4654//                 project
4655//                     .entry_for_path(&(worktree_id, "one.txt").into(), cx)
4656//                     .map(|e| e.id)
4657//             );
4658//         });
4659
4660//         // Add a project folder
4661//         project
4662//             .update(cx, |project, cx| {
4663//                 project.find_or_create_local_worktree("/root2", true, cx)
4664//             })
4665//             .await
4666//             .unwrap();
4667//         assert_eq!(
4668//             window.current_title(cx).as_deref(),
4669//             Some("one.txt β€” root1, root2")
4670//         );
4671
4672//         // Remove a project folder
4673//         project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
4674//         assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root2"));
4675//     }
4676
4677//     #[gpui::test]
4678//     async fn test_close_window(cx: &mut TestAppContext) {
4679//         init_test(cx);
4680
4681//         let fs = FakeFs::new(cx.background());
4682//         fs.insert_tree("/root", json!({ "one": "" })).await;
4683
4684//         let project = Project::test(fs, ["root".as_ref()], cx).await;
4685//         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
4686//         let workspace = window.root(cx);
4687
4688//         // When there are no dirty items, there's nothing to do.
4689//         let item1 = window.build_view(cx, |_| TestItem::new());
4690//         workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
4691//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4692//         assert!(task.await.unwrap());
4693
4694//         // When there are dirty untitled items, prompt to save each one. If the user
4695//         // cancels any prompt, then abort.
4696//         let item2 = window.build_view(cx, |_| TestItem::new().with_dirty(true));
4697//         let item3 = window.build_view(cx, |cx| {
4698//             TestItem::new()
4699//                 .with_dirty(true)
4700//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4701//         });
4702//         workspace.update(cx, |w, cx| {
4703//             w.add_item(Box::new(item2.clone()), cx);
4704//             w.add_item(Box::new(item3.clone()), cx);
4705//         });
4706//         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
4707//         cx.foreground().run_until_parked();
4708//         window.simulate_prompt_answer(2, cx); // cancel save all
4709//         cx.foreground().run_until_parked();
4710//         window.simulate_prompt_answer(2, cx); // cancel save all
4711//         cx.foreground().run_until_parked();
4712//         assert!(!window.has_pending_prompt(cx));
4713//         assert!(!task.await.unwrap());
4714//     }
4715
4716//     #[gpui::test]
4717//     async fn test_close_pane_items(cx: &mut TestAppContext) {
4718//         init_test(cx);
4719
4720//         let fs = FakeFs::new(cx.background());
4721
4722//         let project = Project::test(fs, None, cx).await;
4723//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4724//         let workspace = window.root(cx);
4725
4726//         let item1 = window.build_view(cx, |cx| {
4727//             TestItem::new()
4728//                 .with_dirty(true)
4729//                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4730//         });
4731//         let item2 = window.build_view(cx, |cx| {
4732//             TestItem::new()
4733//                 .with_dirty(true)
4734//                 .with_conflict(true)
4735//                 .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
4736//         });
4737//         let item3 = window.build_view(cx, |cx| {
4738//             TestItem::new()
4739//                 .with_dirty(true)
4740//                 .with_conflict(true)
4741//                 .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
4742//         });
4743//         let item4 = window.build_view(cx, |cx| {
4744//             TestItem::new()
4745//                 .with_dirty(true)
4746//                 .with_project_items(&[TestProjectItem::new_untitled(cx)])
4747//         });
4748//         let pane = workspace.update(cx, |workspace, cx| {
4749//             workspace.add_item(Box::new(item1.clone()), cx);
4750//             workspace.add_item(Box::new(item2.clone()), cx);
4751//             workspace.add_item(Box::new(item3.clone()), cx);
4752//             workspace.add_item(Box::new(item4.clone()), cx);
4753//             workspace.active_pane().clone()
4754//         });
4755
4756//         let close_items = pane.update(cx, |pane, cx| {
4757//             pane.activate_item(1, true, true, cx);
4758//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4759//             let item1_id = item1.id();
4760//             let item3_id = item3.id();
4761//             let item4_id = item4.id();
4762//             pane.close_items(cx, SaveIntent::Close, move |id| {
4763//                 [item1_id, item3_id, item4_id].contains(&id)
4764//             })
4765//         });
4766//         cx.foreground().run_until_parked();
4767
4768//         assert!(window.has_pending_prompt(cx));
4769//         // Ignore "Save all" prompt
4770//         window.simulate_prompt_answer(2, cx);
4771//         cx.foreground().run_until_parked();
4772//         // There's a prompt to save item 1.
4773//         pane.read_with(cx, |pane, _| {
4774//             assert_eq!(pane.items_len(), 4);
4775//             assert_eq!(pane.active_item().unwrap().id(), item1.id());
4776//         });
4777//         // Confirm saving item 1.
4778//         window.simulate_prompt_answer(0, cx);
4779//         cx.foreground().run_until_parked();
4780
4781//         // Item 1 is saved. There's a prompt to save item 3.
4782//         pane.read_with(cx, |pane, cx| {
4783//             assert_eq!(item1.read(cx).save_count, 1);
4784//             assert_eq!(item1.read(cx).save_as_count, 0);
4785//             assert_eq!(item1.read(cx).reload_count, 0);
4786//             assert_eq!(pane.items_len(), 3);
4787//             assert_eq!(pane.active_item().unwrap().id(), item3.id());
4788//         });
4789//         assert!(window.has_pending_prompt(cx));
4790
4791//         // Cancel saving item 3.
4792//         window.simulate_prompt_answer(1, cx);
4793//         cx.foreground().run_until_parked();
4794
4795//         // Item 3 is reloaded. There's a prompt to save item 4.
4796//         pane.read_with(cx, |pane, cx| {
4797//             assert_eq!(item3.read(cx).save_count, 0);
4798//             assert_eq!(item3.read(cx).save_as_count, 0);
4799//             assert_eq!(item3.read(cx).reload_count, 1);
4800//             assert_eq!(pane.items_len(), 2);
4801//             assert_eq!(pane.active_item().unwrap().id(), item4.id());
4802//         });
4803//         assert!(window.has_pending_prompt(cx));
4804
4805//         // Confirm saving item 4.
4806//         window.simulate_prompt_answer(0, cx);
4807//         cx.foreground().run_until_parked();
4808
4809//         // There's a prompt for a path for item 4.
4810//         cx.simulate_new_path_selection(|_| Some(Default::default()));
4811//         close_items.await.unwrap();
4812
4813//         // The requested items are closed.
4814//         pane.read_with(cx, |pane, cx| {
4815//             assert_eq!(item4.read(cx).save_count, 0);
4816//             assert_eq!(item4.read(cx).save_as_count, 1);
4817//             assert_eq!(item4.read(cx).reload_count, 0);
4818//             assert_eq!(pane.items_len(), 1);
4819//             assert_eq!(pane.active_item().unwrap().id(), item2.id());
4820//         });
4821//     }
4822
4823//     #[gpui::test]
4824//     async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) {
4825//         init_test(cx);
4826
4827//         let fs = FakeFs::new(cx.background());
4828
4829//         let project = Project::test(fs, [], cx).await;
4830//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4831//         let workspace = window.root(cx);
4832
4833//         // Create several workspace items with single project entries, and two
4834//         // workspace items with multiple project entries.
4835//         let single_entry_items = (0..=4)
4836//             .map(|project_entry_id| {
4837//                 window.build_view(cx, |cx| {
4838//                     TestItem::new()
4839//                         .with_dirty(true)
4840//                         .with_project_items(&[TestProjectItem::new(
4841//                             project_entry_id,
4842//                             &format!("{project_entry_id}.txt"),
4843//                             cx,
4844//                         )])
4845//                 })
4846//             })
4847//             .collect::<Vec<_>>();
4848//         let item_2_3 = window.build_view(cx, |cx| {
4849//             TestItem::new()
4850//                 .with_dirty(true)
4851//                 .with_singleton(false)
4852//                 .with_project_items(&[
4853//                     single_entry_items[2].read(cx).project_items[0].clone(),
4854//                     single_entry_items[3].read(cx).project_items[0].clone(),
4855//                 ])
4856//         });
4857//         let item_3_4 = window.build_view(cx, |cx| {
4858//             TestItem::new()
4859//                 .with_dirty(true)
4860//                 .with_singleton(false)
4861//                 .with_project_items(&[
4862//                     single_entry_items[3].read(cx).project_items[0].clone(),
4863//                     single_entry_items[4].read(cx).project_items[0].clone(),
4864//                 ])
4865//         });
4866
4867//         // Create two panes that contain the following project entries:
4868//         //   left pane:
4869//         //     multi-entry items:   (2, 3)
4870//         //     single-entry items:  0, 1, 2, 3, 4
4871//         //   right pane:
4872//         //     single-entry items:  1
4873//         //     multi-entry items:   (3, 4)
4874//         let left_pane = workspace.update(cx, |workspace, cx| {
4875//             let left_pane = workspace.active_pane().clone();
4876//             workspace.add_item(Box::new(item_2_3.clone()), cx);
4877//             for item in single_entry_items {
4878//                 workspace.add_item(Box::new(item), cx);
4879//             }
4880//             left_pane.update(cx, |pane, cx| {
4881//                 pane.activate_item(2, true, true, cx);
4882//             });
4883
4884//             workspace
4885//                 .split_and_clone(left_pane.clone(), SplitDirection::Right, cx)
4886//                 .unwrap();
4887
4888//             left_pane
4889//         });
4890
4891//         //Need to cause an effect flush in order to respect new focus
4892//         workspace.update(cx, |workspace, cx| {
4893//             workspace.add_item(Box::new(item_3_4.clone()), cx);
4894//             cx.focus(&left_pane);
4895//         });
4896
4897//         // When closing all of the items in the left pane, we should be prompted twice:
4898//         // once for project entry 0, and once for project entry 2. After those two
4899//         // prompts, the task should complete.
4900
4901//         let close = left_pane.update(cx, |pane, cx| {
4902//             pane.close_items(cx, SaveIntent::Close, move |_| true)
4903//         });
4904//         cx.foreground().run_until_parked();
4905//         // Discard "Save all" prompt
4906//         window.simulate_prompt_answer(2, cx);
4907
4908//         cx.foreground().run_until_parked();
4909//         left_pane.read_with(cx, |pane, cx| {
4910//             assert_eq!(
4911//                 pane.active_item().unwrap().project_entry_ids(cx).as_slice(),
4912//                 &[ProjectEntryId::from_proto(0)]
4913//             );
4914//         });
4915//         window.simulate_prompt_answer(0, 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(2)]
4922//             );
4923//         });
4924//         window.simulate_prompt_answer(0, cx);
4925
4926//         cx.foreground().run_until_parked();
4927//         close.await.unwrap();
4928//         left_pane.read_with(cx, |pane, _| {
4929//             assert_eq!(pane.items_len(), 0);
4930//         });
4931//     }
4932
4933//     #[gpui::test]
4934//     async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
4935//         init_test(cx);
4936
4937//         let fs = FakeFs::new(cx.background());
4938
4939//         let project = Project::test(fs, [], cx).await;
4940//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
4941//         let workspace = window.root(cx);
4942//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
4943
4944//         let item = window.build_view(cx, |cx| {
4945//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
4946//         });
4947//         let item_id = item.id();
4948//         workspace.update(cx, |workspace, cx| {
4949//             workspace.add_item(Box::new(item.clone()), cx);
4950//         });
4951
4952//         // Autosave on window change.
4953//         item.update(cx, |item, cx| {
4954//             cx.update_global(|settings: &mut SettingsStore, cx| {
4955//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
4956//                     settings.autosave = Some(AutosaveSetting::OnWindowChange);
4957//                 })
4958//             });
4959//             item.is_dirty = true;
4960//         });
4961
4962//         // Deactivating the window saves the file.
4963//         window.simulate_deactivation(cx);
4964//         deterministic.run_until_parked();
4965//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
4966
4967//         // Autosave on focus change.
4968//         item.update(cx, |item, cx| {
4969//             cx.focus_self();
4970//             cx.update_global(|settings: &mut SettingsStore, cx| {
4971//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
4972//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
4973//                 })
4974//             });
4975//             item.is_dirty = true;
4976//         });
4977
4978//         // Blurring the item saves the file.
4979//         item.update(cx, |_, cx| cx.blur());
4980//         deterministic.run_until_parked();
4981//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
4982
4983//         // Deactivating the window still saves the file.
4984//         window.simulate_activation(cx);
4985//         item.update(cx, |item, cx| {
4986//             cx.focus_self();
4987//             item.is_dirty = true;
4988//         });
4989//         window.simulate_deactivation(cx);
4990
4991//         deterministic.run_until_parked();
4992//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
4993
4994//         // Autosave after delay.
4995//         item.update(cx, |item, cx| {
4996//             cx.update_global(|settings: &mut SettingsStore, cx| {
4997//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
4998//                     settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
4999//                 })
5000//             });
5001//             item.is_dirty = true;
5002//             cx.emit(TestItemEvent::Edit);
5003//         });
5004
5005//         // Delay hasn't fully expired, so the file is still dirty and unsaved.
5006//         deterministic.advance_clock(Duration::from_millis(250));
5007//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
5008
5009//         // After delay expires, the file is saved.
5010//         deterministic.advance_clock(Duration::from_millis(250));
5011//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 4));
5012
5013//         // Autosave on focus change, ensuring closing the tab counts as such.
5014//         item.update(cx, |item, cx| {
5015//             cx.update_global(|settings: &mut SettingsStore, cx| {
5016//                 settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
5017//                     settings.autosave = Some(AutosaveSetting::OnFocusChange);
5018//                 })
5019//             });
5020//             item.is_dirty = true;
5021//         });
5022
5023//         pane.update(cx, |pane, cx| {
5024//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5025//         })
5026//         .await
5027//         .unwrap();
5028//         assert!(!window.has_pending_prompt(cx));
5029//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5030
5031//         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
5032//         workspace.update(cx, |workspace, cx| {
5033//             workspace.add_item(Box::new(item.clone()), cx);
5034//         });
5035//         item.update(cx, |item, cx| {
5036//             item.project_items[0].update(cx, |item, _| {
5037//                 item.entry_id = None;
5038//             });
5039//             item.is_dirty = true;
5040//             cx.blur();
5041//         });
5042//         deterministic.run_until_parked();
5043//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5044
5045//         // Ensure autosave is prevented for deleted files also when closing the buffer.
5046//         let _close_items = pane.update(cx, |pane, cx| {
5047//             pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
5048//         });
5049//         deterministic.run_until_parked();
5050//         assert!(window.has_pending_prompt(cx));
5051//         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
5052//     }
5053
5054//     #[gpui::test]
5055//     async fn test_pane_navigation(cx: &mut gpui::TestAppContext) {
5056//         init_test(cx);
5057
5058//         let fs = FakeFs::new(cx.background());
5059
5060//         let project = Project::test(fs, [], cx).await;
5061//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5062//         let workspace = window.root(cx);
5063
5064//         let item = window.build_view(cx, |cx| {
5065//             TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
5066//         });
5067//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5068//         let toolbar = pane.read_with(cx, |pane, _| pane.toolbar().clone());
5069//         let toolbar_notify_count = Rc::new(RefCell::new(0));
5070
5071//         workspace.update(cx, |workspace, cx| {
5072//             workspace.add_item(Box::new(item.clone()), cx);
5073//             let toolbar_notification_count = toolbar_notify_count.clone();
5074//             cx.observe(&toolbar, move |_, _, _| {
5075//                 *toolbar_notification_count.borrow_mut() += 1
5076//             })
5077//             .detach();
5078//         });
5079
5080//         pane.read_with(cx, |pane, _| {
5081//             assert!(!pane.can_navigate_backward());
5082//             assert!(!pane.can_navigate_forward());
5083//         });
5084
5085//         item.update(cx, |item, cx| {
5086//             item.set_state("one".to_string(), cx);
5087//         });
5088
5089//         // Toolbar must be notified to re-render the navigation buttons
5090//         assert_eq!(*toolbar_notify_count.borrow(), 1);
5091
5092//         pane.read_with(cx, |pane, _| {
5093//             assert!(pane.can_navigate_backward());
5094//             assert!(!pane.can_navigate_forward());
5095//         });
5096
5097//         workspace
5098//             .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx))
5099//             .await
5100//             .unwrap();
5101
5102//         assert_eq!(*toolbar_notify_count.borrow(), 3);
5103//         pane.read_with(cx, |pane, _| {
5104//             assert!(!pane.can_navigate_backward());
5105//             assert!(pane.can_navigate_forward());
5106//         });
5107//     }
5108
5109//     #[gpui::test]
5110//     async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) {
5111//         init_test(cx);
5112//         let fs = FakeFs::new(cx.background());
5113
5114//         let project = Project::test(fs, [], cx).await;
5115//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5116//         let workspace = window.root(cx);
5117
5118//         let panel = workspace.update(cx, |workspace, cx| {
5119//             let panel = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5120//             workspace.add_panel(panel.clone(), cx);
5121
5122//             workspace
5123//                 .right_dock()
5124//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5125
5126//             panel
5127//         });
5128
5129//         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5130//         pane.update(cx, |pane, cx| {
5131//             let item = cx.build_view(|_| TestItem::new());
5132//             pane.add_item(Box::new(item), true, true, None, cx);
5133//         });
5134
5135//         // Transfer focus from center to panel
5136//         workspace.update(cx, |workspace, cx| {
5137//             workspace.toggle_panel_focus::<TestPanel>(cx);
5138//         });
5139
5140//         workspace.read_with(cx, |workspace, cx| {
5141//             assert!(workspace.right_dock().read(cx).is_open());
5142//             assert!(!panel.is_zoomed(cx));
5143//             assert!(panel.has_focus(cx));
5144//         });
5145
5146//         // Transfer focus from panel to center
5147//         workspace.update(cx, |workspace, cx| {
5148//             workspace.toggle_panel_focus::<TestPanel>(cx);
5149//         });
5150
5151//         workspace.read_with(cx, |workspace, cx| {
5152//             assert!(workspace.right_dock().read(cx).is_open());
5153//             assert!(!panel.is_zoomed(cx));
5154//             assert!(!panel.has_focus(cx));
5155//         });
5156
5157//         // Close the dock
5158//         workspace.update(cx, |workspace, cx| {
5159//             workspace.toggle_dock(DockPosition::Right, cx);
5160//         });
5161
5162//         workspace.read_with(cx, |workspace, cx| {
5163//             assert!(!workspace.right_dock().read(cx).is_open());
5164//             assert!(!panel.is_zoomed(cx));
5165//             assert!(!panel.has_focus(cx));
5166//         });
5167
5168//         // Open the dock
5169//         workspace.update(cx, |workspace, cx| {
5170//             workspace.toggle_dock(DockPosition::Right, cx);
5171//         });
5172
5173//         workspace.read_with(cx, |workspace, cx| {
5174//             assert!(workspace.right_dock().read(cx).is_open());
5175//             assert!(!panel.is_zoomed(cx));
5176//             assert!(panel.has_focus(cx));
5177//         });
5178
5179//         // Focus and zoom panel
5180//         panel.update(cx, |panel, cx| {
5181//             cx.focus_self();
5182//             panel.set_zoomed(true, cx)
5183//         });
5184
5185//         workspace.read_with(cx, |workspace, cx| {
5186//             assert!(workspace.right_dock().read(cx).is_open());
5187//             assert!(panel.is_zoomed(cx));
5188//             assert!(panel.has_focus(cx));
5189//         });
5190
5191//         // Transfer focus to the center closes the dock
5192//         workspace.update(cx, |workspace, cx| {
5193//             workspace.toggle_panel_focus::<TestPanel>(cx);
5194//         });
5195
5196//         workspace.read_with(cx, |workspace, cx| {
5197//             assert!(!workspace.right_dock().read(cx).is_open());
5198//             assert!(panel.is_zoomed(cx));
5199//             assert!(!panel.has_focus(cx));
5200//         });
5201
5202//         // Transferring focus back to the panel keeps it zoomed
5203//         workspace.update(cx, |workspace, cx| {
5204//             workspace.toggle_panel_focus::<TestPanel>(cx);
5205//         });
5206
5207//         workspace.read_with(cx, |workspace, cx| {
5208//             assert!(workspace.right_dock().read(cx).is_open());
5209//             assert!(panel.is_zoomed(cx));
5210//             assert!(panel.has_focus(cx));
5211//         });
5212
5213//         // Close the dock while it is zoomed
5214//         workspace.update(cx, |workspace, cx| {
5215//             workspace.toggle_dock(DockPosition::Right, cx)
5216//         });
5217
5218//         workspace.read_with(cx, |workspace, cx| {
5219//             assert!(!workspace.right_dock().read(cx).is_open());
5220//             assert!(panel.is_zoomed(cx));
5221//             assert!(workspace.zoomed.is_none());
5222//             assert!(!panel.has_focus(cx));
5223//         });
5224
5225//         // Opening the dock, when it's zoomed, retains focus
5226//         workspace.update(cx, |workspace, cx| {
5227//             workspace.toggle_dock(DockPosition::Right, cx)
5228//         });
5229
5230//         workspace.read_with(cx, |workspace, cx| {
5231//             assert!(workspace.right_dock().read(cx).is_open());
5232//             assert!(panel.is_zoomed(cx));
5233//             assert!(workspace.zoomed.is_some());
5234//             assert!(panel.has_focus(cx));
5235//         });
5236
5237//         // Unzoom and close the panel, zoom the active pane.
5238//         panel.update(cx, |panel, cx| panel.set_zoomed(false, cx));
5239//         workspace.update(cx, |workspace, cx| {
5240//             workspace.toggle_dock(DockPosition::Right, cx)
5241//         });
5242//         pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx));
5243
5244//         // Opening a dock unzooms the pane.
5245//         workspace.update(cx, |workspace, cx| {
5246//             workspace.toggle_dock(DockPosition::Right, cx)
5247//         });
5248//         workspace.read_with(cx, |workspace, cx| {
5249//             let pane = pane.read(cx);
5250//             assert!(!pane.is_zoomed());
5251//             assert!(!pane.has_focus());
5252//             assert!(workspace.right_dock().read(cx).is_open());
5253//             assert!(workspace.zoomed.is_none());
5254//         });
5255//     }
5256
5257//     #[gpui::test]
5258//     async fn test_panels(cx: &mut gpui::TestAppContext) {
5259//         init_test(cx);
5260//         let fs = FakeFs::new(cx.background());
5261
5262//         let project = Project::test(fs, [], cx).await;
5263//         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
5264//         let workspace = window.root(cx);
5265
5266//         let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| {
5267//             // Add panel_1 on the left, panel_2 on the right.
5268//             let panel_1 = cx.build_view(|_| TestPanel::new(DockPosition::Left));
5269//             workspace.add_panel(panel_1.clone(), cx);
5270//             workspace
5271//                 .left_dock()
5272//                 .update(cx, |left_dock, cx| left_dock.set_open(true, cx));
5273//             let panel_2 = cx.build_view(|_| TestPanel::new(DockPosition::Right));
5274//             workspace.add_panel(panel_2.clone(), cx);
5275//             workspace
5276//                 .right_dock()
5277//                 .update(cx, |right_dock, cx| right_dock.set_open(true, cx));
5278
5279//             let left_dock = workspace.left_dock();
5280//             assert_eq!(
5281//                 left_dock.read(cx).visible_panel().unwrap().id(),
5282//                 panel_1.id()
5283//             );
5284//             assert_eq!(
5285//                 left_dock.read(cx).active_panel_size(cx).unwrap(),
5286//                 panel_1.size(cx)
5287//             );
5288
5289//             left_dock.update(cx, |left_dock, cx| {
5290//                 left_dock.resize_active_panel(Some(1337.), cx)
5291//             });
5292//             assert_eq!(
5293//                 workspace
5294//                     .right_dock()
5295//                     .read(cx)
5296//                     .visible_panel()
5297//                     .unwrap()
5298//                     .id(),
5299//                 panel_2.id()
5300//             );
5301
5302//             (panel_1, panel_2)
5303//         });
5304
5305//         // Move panel_1 to the right
5306//         panel_1.update(cx, |panel_1, cx| {
5307//             panel_1.set_position(DockPosition::Right, cx)
5308//         });
5309
5310//         workspace.update(cx, |workspace, cx| {
5311//             // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right.
5312//             // Since it was the only panel on the left, the left dock should now be closed.
5313//             assert!(!workspace.left_dock().read(cx).is_open());
5314//             assert!(workspace.left_dock().read(cx).visible_panel().is_none());
5315//             let right_dock = workspace.right_dock();
5316//             assert_eq!(
5317//                 right_dock.read(cx).visible_panel().unwrap().id(),
5318//                 panel_1.id()
5319//             );
5320//             assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5321
5322//             // Now we move panel_2Β to the left
5323//             panel_2.set_position(DockPosition::Left, cx);
5324//         });
5325
5326//         workspace.update(cx, |workspace, cx| {
5327//             // Since panel_2 was not visible on the right, we don't open the left dock.
5328//             assert!(!workspace.left_dock().read(cx).is_open());
5329//             // And the right dock is unaffected in it's displaying of panel_1
5330//             assert!(workspace.right_dock().read(cx).is_open());
5331//             assert_eq!(
5332//                 workspace
5333//                     .right_dock()
5334//                     .read(cx)
5335//                     .visible_panel()
5336//                     .unwrap()
5337//                     .id(),
5338//                 panel_1.id()
5339//             );
5340//         });
5341
5342//         // Move panel_1 back to the left
5343//         panel_1.update(cx, |panel_1, cx| {
5344//             panel_1.set_position(DockPosition::Left, cx)
5345//         });
5346
5347//         workspace.update(cx, |workspace, cx| {
5348//             // Since panel_1 was visible on the right, we open the left dock and make panel_1 active.
5349//             let left_dock = workspace.left_dock();
5350//             assert!(left_dock.read(cx).is_open());
5351//             assert_eq!(
5352//                 left_dock.read(cx).visible_panel().unwrap().id(),
5353//                 panel_1.id()
5354//             );
5355//             assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
5356//             // And right the dock should be closed as it no longer has any panels.
5357//             assert!(!workspace.right_dock().read(cx).is_open());
5358
5359//             // Now we move panel_1 to the bottom
5360//             panel_1.set_position(DockPosition::Bottom, cx);
5361//         });
5362
5363//         workspace.update(cx, |workspace, cx| {
5364//             // Since panel_1 was visible on the left, we close the left dock.
5365//             assert!(!workspace.left_dock().read(cx).is_open());
5366//             // The bottom dock is sized based on the panel's default size,
5367//             // since the panel orientation changed from vertical to horizontal.
5368//             let bottom_dock = workspace.bottom_dock();
5369//             assert_eq!(
5370//                 bottom_dock.read(cx).active_panel_size(cx).unwrap(),
5371//                 panel_1.size(cx),
5372//             );
5373//             // Close bottom dock and move panel_1 back to the left.
5374//             bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx));
5375//             panel_1.set_position(DockPosition::Left, cx);
5376//         });
5377
5378//         // Emit activated event on panel 1
5379//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Activated));
5380
5381//         // Now the left dock is open and panel_1 is active and focused.
5382//         workspace.read_with(cx, |workspace, cx| {
5383//             let left_dock = workspace.left_dock();
5384//             assert!(left_dock.read(cx).is_open());
5385//             assert_eq!(
5386//                 left_dock.read(cx).visible_panel().unwrap().id(),
5387//                 panel_1.id()
5388//             );
5389//             assert!(panel_1.is_focused(cx));
5390//         });
5391
5392//         // Emit closed event on panel 2, which is not active
5393//         panel_2.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5394
5395//         // Wo don't close the left dock, because panel_2 wasn't the active panel
5396//         workspace.read_with(cx, |workspace, cx| {
5397//             let left_dock = workspace.left_dock();
5398//             assert!(left_dock.read(cx).is_open());
5399//             assert_eq!(
5400//                 left_dock.read(cx).visible_panel().unwrap().id(),
5401//                 panel_1.id()
5402//             );
5403//         });
5404
5405//         // Emitting a ZoomIn event shows the panel as zoomed.
5406//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn));
5407//         workspace.read_with(cx, |workspace, _| {
5408//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5409//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Left));
5410//         });
5411
5412//         // Move panel to another dock while it is zoomed
5413//         panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx));
5414//         workspace.read_with(cx, |workspace, _| {
5415//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5416//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5417//         });
5418
5419//         // If focus is transferred to another view that's not a panel or another pane, we still show
5420//         // the panel as zoomed.
5421//         let focus_receiver = window.build_view(cx, |_| EmptyView);
5422//         focus_receiver.update(cx, |_, cx| cx.focus_self());
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 elsewhere in the workspace, the panel is no longer zoomed.
5429//         workspace.update(cx, |_, cx| cx.focus_self());
5430//         workspace.read_with(cx, |workspace, _| {
5431//             assert_eq!(workspace.zoomed, None);
5432//             assert_eq!(workspace.zoomed_position, None);
5433//         });
5434
5435//         // If focus is transferred again to another view that's not a panel or a pane, we won't
5436//         // show the panel as zoomed because it wasn't zoomed before.
5437//         focus_receiver.update(cx, |_, cx| cx.focus_self());
5438//         workspace.read_with(cx, |workspace, _| {
5439//             assert_eq!(workspace.zoomed, None);
5440//             assert_eq!(workspace.zoomed_position, None);
5441//         });
5442
5443//         // When focus is transferred back to the panel, it is zoomed again.
5444//         panel_1.update(cx, |_, cx| cx.focus_self());
5445//         workspace.read_with(cx, |workspace, _| {
5446//             assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
5447//             assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
5448//         });
5449
5450//         // Emitting a ZoomOut event unzooms the panel.
5451//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut));
5452//         workspace.read_with(cx, |workspace, _| {
5453//             assert_eq!(workspace.zoomed, None);
5454//             assert_eq!(workspace.zoomed_position, None);
5455//         });
5456
5457//         // Emit closed event on panel 1, which is active
5458//         panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed));
5459
5460//         // Now the left dock is closed, because panel_1 was the active panel
5461//         workspace.read_with(cx, |workspace, cx| {
5462//             let right_dock = workspace.right_dock();
5463//             assert!(!right_dock.read(cx).is_open());
5464//         });
5465//     }
5466
5467//     pub fn init_test(cx: &mut TestAppContext) {
5468//         cx.foreground().forbid_parking();
5469//         cx.update(|cx| {
5470//             cx.set_global(SettingsStore::test(cx));
5471//             theme::init((), cx);
5472//             language::init(cx);
5473//             crate::init_settings(cx);
5474//             Project::init_settings(cx);
5475//         });
5476//     }
5477// }