workspace.rs

   1pub mod lsp_status;
   2pub mod menu;
   3pub mod pane;
   4pub mod pane_group;
   5pub mod sidebar;
   6mod status_bar;
   7mod toolbar;
   8mod waiting_room;
   9
  10use anyhow::{anyhow, Context, Result};
  11use client::{
  12    proto, Authenticate, Client, Contact, PeerId, Subscription, TypedEnvelope, User, UserStore,
  13};
  14use clock::ReplicaId;
  15use collections::{hash_map, HashMap, HashSet};
  16use gpui::{
  17    actions,
  18    color::Color,
  19    elements::*,
  20    geometry::{rect::RectF, vector::vec2f, PathBuilder},
  21    impl_internal_actions,
  22    json::{self, ToJson},
  23    platform::{CursorStyle, WindowOptions},
  24    AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, Entity, ImageData,
  25    ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View,
  26    ViewContext, ViewHandle, WeakViewHandle,
  27};
  28use language::LanguageRegistry;
  29use log::error;
  30pub use pane::*;
  31pub use pane_group::*;
  32use postage::prelude::Stream;
  33use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
  34use settings::Settings;
  35use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem, ToggleSidebarItemFocus};
  36use status_bar::StatusBar;
  37pub use status_bar::StatusItemView;
  38use std::{
  39    any::{Any, TypeId},
  40    cell::RefCell,
  41    fmt,
  42    future::Future,
  43    path::{Path, PathBuf},
  44    rc::Rc,
  45    sync::{
  46        atomic::{AtomicBool, Ordering::SeqCst},
  47        Arc,
  48    },
  49};
  50use theme::{Theme, ThemeRegistry};
  51pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
  52use util::ResultExt;
  53use waiting_room::WaitingRoom;
  54
  55type ProjectItemBuilders = HashMap<
  56    TypeId,
  57    fn(usize, ModelHandle<Project>, AnyModelHandle, &mut MutableAppContext) -> Box<dyn ItemHandle>,
  58>;
  59
  60type FollowableItemBuilder = fn(
  61    ViewHandle<Pane>,
  62    ModelHandle<Project>,
  63    &mut Option<proto::view::Variant>,
  64    &mut MutableAppContext,
  65) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>;
  66type FollowableItemBuilders = HashMap<
  67    TypeId,
  68    (
  69        FollowableItemBuilder,
  70        fn(AnyViewHandle) -> Box<dyn FollowableItemHandle>,
  71    ),
  72>;
  73
  74actions!(
  75    workspace,
  76    [
  77        Open,
  78        OpenNew,
  79        Unfollow,
  80        Save,
  81        ActivatePreviousPane,
  82        ActivateNextPane,
  83        FollowNextCollaborator,
  84    ]
  85);
  86
  87#[derive(Clone)]
  88pub struct OpenPaths {
  89    pub paths: Vec<PathBuf>,
  90}
  91
  92#[derive(Clone)]
  93pub struct ToggleFollow(pub PeerId);
  94
  95#[derive(Clone)]
  96pub struct JoinProject {
  97    pub contact: Arc<Contact>,
  98    pub project_index: usize,
  99}
 100
 101impl_internal_actions!(workspace, [OpenPaths, ToggleFollow, JoinProject]);
 102
 103pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
 104    pane::init(cx);
 105
 106    cx.add_global_action(open);
 107    cx.add_global_action({
 108        let app_state = Arc::downgrade(&app_state);
 109        move |action: &OpenPaths, cx: &mut MutableAppContext| {
 110            if let Some(app_state) = app_state.upgrade() {
 111                open_paths(&action.paths, &app_state, cx).detach();
 112            }
 113        }
 114    });
 115    cx.add_global_action({
 116        let app_state = Arc::downgrade(&app_state);
 117        move |_: &OpenNew, cx: &mut MutableAppContext| {
 118            if let Some(app_state) = app_state.upgrade() {
 119                open_new(&app_state, cx)
 120            }
 121        }
 122    });
 123    cx.add_global_action({
 124        let app_state = Arc::downgrade(&app_state);
 125        move |action: &JoinProject, cx: &mut MutableAppContext| {
 126            if let Some(app_state) = app_state.upgrade() {
 127                join_project(action.contact.clone(), action.project_index, &app_state, cx);
 128            }
 129        }
 130    });
 131
 132    cx.add_async_action(Workspace::toggle_follow);
 133    cx.add_async_action(Workspace::follow_next_collaborator);
 134    cx.add_action(
 135        |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
 136            let pane = workspace.active_pane().clone();
 137            workspace.unfollow(&pane, cx);
 138        },
 139    );
 140    cx.add_action(
 141        |workspace: &mut Workspace, _: &Save, cx: &mut ViewContext<Workspace>| {
 142            workspace.save_active_item(cx).detach_and_log_err(cx);
 143        },
 144    );
 145    cx.add_action(Workspace::toggle_sidebar_item);
 146    cx.add_action(Workspace::toggle_sidebar_item_focus);
 147    cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
 148        workspace.activate_previous_pane(cx)
 149    });
 150    cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
 151        workspace.activate_next_pane(cx)
 152    });
 153
 154    let client = &app_state.client;
 155    client.add_view_request_handler(Workspace::handle_follow);
 156    client.add_view_message_handler(Workspace::handle_unfollow);
 157    client.add_view_message_handler(Workspace::handle_update_followers);
 158}
 159
 160pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
 161    cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
 162        builders.insert(TypeId::of::<I::Item>(), |window_id, project, model, cx| {
 163            let item = model.downcast::<I::Item>().unwrap();
 164            Box::new(cx.add_view(window_id, |cx| I::for_project_item(project, item, cx)))
 165        });
 166    });
 167}
 168
 169pub fn register_followable_item<I: FollowableItem>(cx: &mut MutableAppContext) {
 170    cx.update_default_global(|builders: &mut FollowableItemBuilders, _| {
 171        builders.insert(
 172            TypeId::of::<I>(),
 173            (
 174                |pane, project, state, cx| {
 175                    I::from_state_proto(pane, project, state, cx).map(|task| {
 176                        cx.foreground()
 177                            .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
 178                    })
 179                },
 180                |this| Box::new(this.downcast::<I>().unwrap()),
 181            ),
 182        );
 183    });
 184}
 185
 186pub struct AppState {
 187    pub languages: Arc<LanguageRegistry>,
 188    pub themes: Arc<ThemeRegistry>,
 189    pub client: Arc<client::Client>,
 190    pub user_store: ModelHandle<client::UserStore>,
 191    pub fs: Arc<dyn fs::Fs>,
 192    pub build_window_options: fn() -> WindowOptions<'static>,
 193    pub build_workspace:
 194        fn(ModelHandle<Project>, &Arc<AppState>, &mut ViewContext<Workspace>) -> Workspace,
 195}
 196
 197pub trait Item: View {
 198    fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
 199    fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
 200        false
 201    }
 202    fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
 203    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 204    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
 205    fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>);
 206    fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
 207    where
 208        Self: Sized,
 209    {
 210        None
 211    }
 212    fn is_dirty(&self, _: &AppContext) -> bool {
 213        false
 214    }
 215    fn has_conflict(&self, _: &AppContext) -> bool {
 216        false
 217    }
 218    fn can_save(&self, cx: &AppContext) -> bool;
 219    fn save(
 220        &mut self,
 221        project: ModelHandle<Project>,
 222        cx: &mut ViewContext<Self>,
 223    ) -> Task<Result<()>>;
 224    fn can_save_as(&self, cx: &AppContext) -> bool;
 225    fn save_as(
 226        &mut self,
 227        project: ModelHandle<Project>,
 228        abs_path: PathBuf,
 229        cx: &mut ViewContext<Self>,
 230    ) -> Task<Result<()>>;
 231    fn reload(
 232        &mut self,
 233        project: ModelHandle<Project>,
 234        cx: &mut ViewContext<Self>,
 235    ) -> Task<Result<()>>;
 236    fn should_activate_item_on_event(_: &Self::Event) -> bool {
 237        false
 238    }
 239    fn should_close_item_on_event(_: &Self::Event) -> bool {
 240        false
 241    }
 242    fn should_update_tab_on_event(_: &Self::Event) -> bool {
 243        false
 244    }
 245    fn act_as_type(
 246        &self,
 247        type_id: TypeId,
 248        self_handle: &ViewHandle<Self>,
 249        _: &AppContext,
 250    ) -> Option<AnyViewHandle> {
 251        if TypeId::of::<Self>() == type_id {
 252            Some(self_handle.into())
 253        } else {
 254            None
 255        }
 256    }
 257}
 258
 259pub trait ProjectItem: Item {
 260    type Item: project::Item;
 261
 262    fn for_project_item(
 263        project: ModelHandle<Project>,
 264        item: ModelHandle<Self::Item>,
 265        cx: &mut ViewContext<Self>,
 266    ) -> Self;
 267}
 268
 269pub trait FollowableItem: Item {
 270    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
 271    fn from_state_proto(
 272        pane: ViewHandle<Pane>,
 273        project: ModelHandle<Project>,
 274        state: &mut Option<proto::view::Variant>,
 275        cx: &mut MutableAppContext,
 276    ) -> Option<Task<Result<ViewHandle<Self>>>>;
 277    fn add_event_to_update_proto(
 278        &self,
 279        event: &Self::Event,
 280        update: &mut Option<proto::update_view::Variant>,
 281        cx: &AppContext,
 282    ) -> bool;
 283    fn apply_update_proto(
 284        &mut self,
 285        message: proto::update_view::Variant,
 286        cx: &mut ViewContext<Self>,
 287    ) -> Result<()>;
 288
 289    fn set_leader_replica_id(&mut self, leader_replica_id: Option<u16>, cx: &mut ViewContext<Self>);
 290    fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool;
 291}
 292
 293pub trait FollowableItemHandle: ItemHandle {
 294    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut MutableAppContext);
 295    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
 296    fn add_event_to_update_proto(
 297        &self,
 298        event: &dyn Any,
 299        update: &mut Option<proto::update_view::Variant>,
 300        cx: &AppContext,
 301    ) -> bool;
 302    fn apply_update_proto(
 303        &self,
 304        message: proto::update_view::Variant,
 305        cx: &mut MutableAppContext,
 306    ) -> Result<()>;
 307    fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
 308}
 309
 310impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
 311    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut MutableAppContext) {
 312        self.update(cx, |this, cx| {
 313            this.set_leader_replica_id(leader_replica_id, cx)
 314        })
 315    }
 316
 317    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
 318        self.read(cx).to_state_proto(cx)
 319    }
 320
 321    fn add_event_to_update_proto(
 322        &self,
 323        event: &dyn Any,
 324        update: &mut Option<proto::update_view::Variant>,
 325        cx: &AppContext,
 326    ) -> bool {
 327        if let Some(event) = event.downcast_ref() {
 328            self.read(cx).add_event_to_update_proto(event, update, cx)
 329        } else {
 330            false
 331        }
 332    }
 333
 334    fn apply_update_proto(
 335        &self,
 336        message: proto::update_view::Variant,
 337        cx: &mut MutableAppContext,
 338    ) -> Result<()> {
 339        self.update(cx, |this, cx| this.apply_update_proto(message, cx))
 340    }
 341
 342    fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
 343        if let Some(event) = event.downcast_ref() {
 344            T::should_unfollow_on_event(event, cx)
 345        } else {
 346            false
 347        }
 348    }
 349}
 350
 351pub trait ItemHandle: 'static + fmt::Debug {
 352    fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
 353    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 354    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
 355    fn boxed_clone(&self) -> Box<dyn ItemHandle>;
 356    fn set_nav_history(&self, nav_history: Rc<RefCell<NavHistory>>, cx: &mut MutableAppContext);
 357    fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>>;
 358    fn added_to_pane(
 359        &self,
 360        workspace: &mut Workspace,
 361        pane: ViewHandle<Pane>,
 362        cx: &mut ViewContext<Workspace>,
 363    );
 364    fn deactivated(&self, cx: &mut MutableAppContext);
 365    fn navigate(&self, data: Box<dyn Any>, cx: &mut MutableAppContext) -> bool;
 366    fn id(&self) -> usize;
 367    fn to_any(&self) -> AnyViewHandle;
 368    fn is_dirty(&self, cx: &AppContext) -> bool;
 369    fn has_conflict(&self, cx: &AppContext) -> bool;
 370    fn can_save(&self, cx: &AppContext) -> bool;
 371    fn can_save_as(&self, cx: &AppContext) -> bool;
 372    fn save(&self, project: ModelHandle<Project>, cx: &mut MutableAppContext) -> Task<Result<()>>;
 373    fn save_as(
 374        &self,
 375        project: ModelHandle<Project>,
 376        abs_path: PathBuf,
 377        cx: &mut MutableAppContext,
 378    ) -> Task<Result<()>>;
 379    fn reload(&self, project: ModelHandle<Project>, cx: &mut MutableAppContext)
 380        -> Task<Result<()>>;
 381    fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle>;
 382    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
 383    fn on_release(
 384        &self,
 385        cx: &mut MutableAppContext,
 386        callback: Box<dyn FnOnce(&mut MutableAppContext)>,
 387    ) -> gpui::Subscription;
 388}
 389
 390pub trait WeakItemHandle {
 391    fn id(&self) -> usize;
 392    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
 393}
 394
 395impl dyn ItemHandle {
 396    pub fn downcast<T: View>(&self) -> Option<ViewHandle<T>> {
 397        self.to_any().downcast()
 398    }
 399
 400    pub fn act_as<T: View>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
 401        self.act_as_type(TypeId::of::<T>(), cx)
 402            .and_then(|t| t.downcast())
 403    }
 404}
 405
 406impl<T: Item> ItemHandle for ViewHandle<T> {
 407    fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
 408        self.read(cx).tab_content(style, cx)
 409    }
 410
 411    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
 412        self.read(cx).project_path(cx)
 413    }
 414
 415    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
 416        self.read(cx).project_entry_id(cx)
 417    }
 418
 419    fn boxed_clone(&self) -> Box<dyn ItemHandle> {
 420        Box::new(self.clone())
 421    }
 422
 423    fn set_nav_history(&self, nav_history: Rc<RefCell<NavHistory>>, cx: &mut MutableAppContext) {
 424        self.update(cx, |item, cx| {
 425            item.set_nav_history(ItemNavHistory::new(nav_history, &cx.handle()), cx);
 426        })
 427    }
 428
 429    fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>> {
 430        self.update(cx, |item, cx| {
 431            cx.add_option_view(|cx| item.clone_on_split(cx))
 432        })
 433        .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
 434    }
 435
 436    fn added_to_pane(
 437        &self,
 438        workspace: &mut Workspace,
 439        pane: ViewHandle<Pane>,
 440        cx: &mut ViewContext<Workspace>,
 441    ) {
 442        if let Some(followed_item) = self.to_followable_item_handle(cx) {
 443            if let Some(message) = followed_item.to_state_proto(cx) {
 444                workspace.update_followers(
 445                    proto::update_followers::Variant::CreateView(proto::View {
 446                        id: followed_item.id() as u64,
 447                        variant: Some(message),
 448                        leader_id: workspace.leader_for_pane(&pane).map(|id| id.0),
 449                    }),
 450                    cx,
 451                );
 452            }
 453        }
 454
 455        let pending_update = Rc::new(RefCell::new(None));
 456        let pending_update_scheduled = Rc::new(AtomicBool::new(false));
 457        let pane = pane.downgrade();
 458        cx.subscribe(self, move |workspace, item, event, cx| {
 459            let pane = if let Some(pane) = pane.upgrade(cx) {
 460                pane
 461            } else {
 462                log::error!("unexpected item event after pane was dropped");
 463                return;
 464            };
 465
 466            if let Some(item) = item.to_followable_item_handle(cx) {
 467                let leader_id = workspace.leader_for_pane(&pane);
 468
 469                if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
 470                    workspace.unfollow(&pane, cx);
 471                }
 472
 473                if item.add_event_to_update_proto(event, &mut *pending_update.borrow_mut(), cx)
 474                    && !pending_update_scheduled.load(SeqCst)
 475                {
 476                    pending_update_scheduled.store(true, SeqCst);
 477                    cx.after_window_update({
 478                        let pending_update = pending_update.clone();
 479                        let pending_update_scheduled = pending_update_scheduled.clone();
 480                        move |this, cx| {
 481                            pending_update_scheduled.store(false, SeqCst);
 482                            this.update_followers(
 483                                proto::update_followers::Variant::UpdateView(proto::UpdateView {
 484                                    id: item.id() as u64,
 485                                    variant: pending_update.borrow_mut().take(),
 486                                    leader_id: leader_id.map(|id| id.0),
 487                                }),
 488                                cx,
 489                            );
 490                        }
 491                    });
 492                }
 493            }
 494
 495            if T::should_close_item_on_event(event) {
 496                Pane::close_item(workspace, pane, item.id(), cx).detach_and_log_err(cx);
 497                return;
 498            }
 499
 500            if T::should_activate_item_on_event(event) {
 501                pane.update(cx, |pane, cx| {
 502                    if let Some(ix) = pane.index_for_item(&item) {
 503                        pane.activate_item(ix, true, true, cx);
 504                        pane.activate(cx);
 505                    }
 506                });
 507            }
 508
 509            if T::should_update_tab_on_event(event) {
 510                pane.update(cx, |_, cx| cx.notify());
 511            }
 512        })
 513        .detach();
 514    }
 515
 516    fn deactivated(&self, cx: &mut MutableAppContext) {
 517        self.update(cx, |this, cx| this.deactivated(cx));
 518    }
 519
 520    fn navigate(&self, data: Box<dyn Any>, cx: &mut MutableAppContext) -> bool {
 521        self.update(cx, |this, cx| this.navigate(data, cx))
 522    }
 523
 524    fn id(&self) -> usize {
 525        self.id()
 526    }
 527
 528    fn to_any(&self) -> AnyViewHandle {
 529        self.into()
 530    }
 531
 532    fn is_dirty(&self, cx: &AppContext) -> bool {
 533        self.read(cx).is_dirty(cx)
 534    }
 535
 536    fn has_conflict(&self, cx: &AppContext) -> bool {
 537        self.read(cx).has_conflict(cx)
 538    }
 539
 540    fn can_save(&self, cx: &AppContext) -> bool {
 541        self.read(cx).can_save(cx)
 542    }
 543
 544    fn can_save_as(&self, cx: &AppContext) -> bool {
 545        self.read(cx).can_save_as(cx)
 546    }
 547
 548    fn save(&self, project: ModelHandle<Project>, cx: &mut MutableAppContext) -> Task<Result<()>> {
 549        self.update(cx, |item, cx| item.save(project, cx))
 550    }
 551
 552    fn save_as(
 553        &self,
 554        project: ModelHandle<Project>,
 555        abs_path: PathBuf,
 556        cx: &mut MutableAppContext,
 557    ) -> Task<anyhow::Result<()>> {
 558        self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
 559    }
 560
 561    fn reload(
 562        &self,
 563        project: ModelHandle<Project>,
 564        cx: &mut MutableAppContext,
 565    ) -> Task<Result<()>> {
 566        self.update(cx, |item, cx| item.reload(project, cx))
 567    }
 568
 569    fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle> {
 570        self.read(cx).act_as_type(type_id, self, cx)
 571    }
 572
 573    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
 574        if cx.has_global::<FollowableItemBuilders>() {
 575            let builders = cx.global::<FollowableItemBuilders>();
 576            let item = self.to_any();
 577            Some(builders.get(&item.view_type())?.1(item))
 578        } else {
 579            None
 580        }
 581    }
 582
 583    fn on_release(
 584        &self,
 585        cx: &mut MutableAppContext,
 586        callback: Box<dyn FnOnce(&mut MutableAppContext)>,
 587    ) -> gpui::Subscription {
 588        cx.observe_release(self, move |_, cx| callback(cx))
 589    }
 590}
 591
 592impl Into<AnyViewHandle> for Box<dyn ItemHandle> {
 593    fn into(self) -> AnyViewHandle {
 594        self.to_any()
 595    }
 596}
 597
 598impl Clone for Box<dyn ItemHandle> {
 599    fn clone(&self) -> Box<dyn ItemHandle> {
 600        self.boxed_clone()
 601    }
 602}
 603
 604impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
 605    fn id(&self) -> usize {
 606        self.id()
 607    }
 608
 609    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
 610        self.upgrade(cx).map(|v| Box::new(v) as Box<dyn ItemHandle>)
 611    }
 612}
 613
 614pub trait Notification: View {
 615    fn should_dismiss_notification_on_event(&self, event: &<Self as Entity>::Event) -> bool;
 616}
 617
 618pub trait NotificationHandle {
 619    fn id(&self) -> usize;
 620    fn to_any(&self) -> AnyViewHandle;
 621}
 622
 623impl<T: Notification> NotificationHandle for ViewHandle<T> {
 624    fn id(&self) -> usize {
 625        self.id()
 626    }
 627
 628    fn to_any(&self) -> AnyViewHandle {
 629        self.into()
 630    }
 631}
 632
 633impl Into<AnyViewHandle> for &dyn NotificationHandle {
 634    fn into(self) -> AnyViewHandle {
 635        self.to_any()
 636    }
 637}
 638
 639impl AppState {
 640    #[cfg(any(test, feature = "test-support"))]
 641    pub fn test(cx: &mut MutableAppContext) -> Arc<Self> {
 642        let settings = Settings::test(cx);
 643        cx.set_global(settings);
 644
 645        let fs = project::FakeFs::new(cx.background().clone());
 646        let languages = Arc::new(LanguageRegistry::test());
 647        let http_client = client::test::FakeHttpClient::with_404_response();
 648        let client = Client::new(http_client.clone());
 649        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
 650        let themes = ThemeRegistry::new((), cx.font_cache().clone());
 651        Arc::new(Self {
 652            client,
 653            themes,
 654            fs,
 655            languages,
 656            user_store,
 657            build_workspace: |project, _, cx| Workspace::new(project, cx),
 658            build_window_options: || Default::default(),
 659        })
 660    }
 661}
 662
 663pub enum Event {
 664    PaneAdded(ViewHandle<Pane>),
 665    ContactRequestedJoin(u64),
 666}
 667
 668pub struct Workspace {
 669    weak_self: WeakViewHandle<Self>,
 670    client: Arc<Client>,
 671    user_store: ModelHandle<client::UserStore>,
 672    remote_entity_subscription: Option<Subscription>,
 673    fs: Arc<dyn Fs>,
 674    modal: Option<AnyViewHandle>,
 675    center: PaneGroup,
 676    left_sidebar: ViewHandle<Sidebar>,
 677    right_sidebar: ViewHandle<Sidebar>,
 678    panes: Vec<ViewHandle<Pane>>,
 679    active_pane: ViewHandle<Pane>,
 680    status_bar: ViewHandle<StatusBar>,
 681    notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
 682    project: ModelHandle<Project>,
 683    leader_state: LeaderState,
 684    follower_states_by_leader: FollowerStatesByLeader,
 685    last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
 686    _observe_current_user: Task<()>,
 687}
 688
 689#[derive(Default)]
 690struct LeaderState {
 691    followers: HashSet<PeerId>,
 692}
 693
 694type FollowerStatesByLeader = HashMap<PeerId, HashMap<ViewHandle<Pane>, FollowerState>>;
 695
 696#[derive(Default)]
 697struct FollowerState {
 698    active_view_id: Option<u64>,
 699    items_by_leader_view_id: HashMap<u64, FollowerItem>,
 700}
 701
 702#[derive(Debug)]
 703enum FollowerItem {
 704    Loading(Vec<proto::update_view::Variant>),
 705    Loaded(Box<dyn FollowableItemHandle>),
 706}
 707
 708impl Workspace {
 709    pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
 710        cx.observe(&project, |_, project, cx| {
 711            if project.read(cx).is_read_only() {
 712                cx.blur();
 713            }
 714            cx.notify()
 715        })
 716        .detach();
 717
 718        cx.subscribe(&project, move |this, project, event, cx| {
 719            match event {
 720                project::Event::RemoteIdChanged(remote_id) => {
 721                    this.project_remote_id_changed(*remote_id, cx);
 722                }
 723                project::Event::CollaboratorLeft(peer_id) => {
 724                    this.collaborator_left(*peer_id, cx);
 725                }
 726                _ => {}
 727            }
 728            if project.read(cx).is_read_only() {
 729                cx.blur();
 730            }
 731            cx.notify()
 732        })
 733        .detach();
 734
 735        let pane = cx.add_view(|cx| Pane::new(cx));
 736        let pane_id = pane.id();
 737        cx.observe(&pane, move |me, _, cx| {
 738            let active_entry = me.active_project_path(cx);
 739            me.project
 740                .update(cx, |project, cx| project.set_active_path(active_entry, cx));
 741        })
 742        .detach();
 743        cx.subscribe(&pane, move |me, _, event, cx| {
 744            me.handle_pane_event(pane_id, event, cx)
 745        })
 746        .detach();
 747        cx.focus(&pane);
 748        cx.emit(Event::PaneAdded(pane.clone()));
 749
 750        let fs = project.read(cx).fs().clone();
 751        let user_store = project.read(cx).user_store();
 752        let client = project.read(cx).client();
 753        let mut current_user = user_store.read(cx).watch_current_user().clone();
 754        let mut connection_status = client.status().clone();
 755        let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
 756            current_user.recv().await;
 757            connection_status.recv().await;
 758            let mut stream =
 759                Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
 760
 761            while stream.recv().await.is_some() {
 762                cx.update(|cx| {
 763                    if let Some(this) = this.upgrade(cx) {
 764                        this.update(cx, |_, cx| cx.notify());
 765                    }
 766                })
 767            }
 768        });
 769
 770        let weak_self = cx.weak_handle();
 771
 772        cx.emit_global(WorkspaceCreated(weak_self.clone()));
 773
 774        let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
 775        let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
 776        let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx));
 777        let right_sidebar_buttons =
 778            cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx));
 779        let status_bar = cx.add_view(|cx| {
 780            let mut status_bar = StatusBar::new(&pane.clone(), cx);
 781            status_bar.add_left_item(left_sidebar_buttons, cx);
 782            status_bar.add_right_item(right_sidebar_buttons, cx);
 783            status_bar
 784        });
 785
 786        let mut this = Workspace {
 787            modal: None,
 788            weak_self,
 789            center: PaneGroup::new(pane.clone()),
 790            panes: vec![pane.clone()],
 791            active_pane: pane.clone(),
 792            status_bar,
 793            notifications: Default::default(),
 794            client,
 795            remote_entity_subscription: None,
 796            user_store,
 797            fs,
 798            left_sidebar,
 799            right_sidebar,
 800            project,
 801            leader_state: Default::default(),
 802            follower_states_by_leader: Default::default(),
 803            last_leaders_by_pane: Default::default(),
 804            _observe_current_user,
 805        };
 806        this.project_remote_id_changed(this.project.read(cx).remote_id(), cx);
 807        this
 808    }
 809
 810    pub fn weak_handle(&self) -> WeakViewHandle<Self> {
 811        self.weak_self.clone()
 812    }
 813
 814    pub fn left_sidebar(&self) -> &ViewHandle<Sidebar> {
 815        &self.left_sidebar
 816    }
 817
 818    pub fn right_sidebar(&self) -> &ViewHandle<Sidebar> {
 819        &self.right_sidebar
 820    }
 821
 822    pub fn status_bar(&self) -> &ViewHandle<StatusBar> {
 823        &self.status_bar
 824    }
 825
 826    pub fn user_store(&self) -> &ModelHandle<UserStore> {
 827        &self.user_store
 828    }
 829
 830    pub fn project(&self) -> &ModelHandle<Project> {
 831        &self.project
 832    }
 833
 834    pub fn worktrees<'a>(
 835        &self,
 836        cx: &'a AppContext,
 837    ) -> impl 'a + Iterator<Item = ModelHandle<Worktree>> {
 838        self.project.read(cx).worktrees(cx)
 839    }
 840
 841    pub fn contains_paths(&self, paths: &[PathBuf], cx: &AppContext) -> bool {
 842        paths.iter().all(|path| self.contains_path(&path, cx))
 843    }
 844
 845    pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool {
 846        for worktree in self.worktrees(cx) {
 847            let worktree = worktree.read(cx).as_local();
 848            if worktree.map_or(false, |w| w.contains_abs_path(path)) {
 849                return true;
 850            }
 851        }
 852        false
 853    }
 854
 855    pub fn worktree_scans_complete(&self, cx: &AppContext) -> impl Future<Output = ()> + 'static {
 856        let futures = self
 857            .worktrees(cx)
 858            .filter_map(|worktree| worktree.read(cx).as_local())
 859            .map(|worktree| worktree.scan_complete())
 860            .collect::<Vec<_>>();
 861        async move {
 862            for future in futures {
 863                future.await;
 864            }
 865        }
 866    }
 867
 868    pub fn open_paths(
 869        &mut self,
 870        mut abs_paths: Vec<PathBuf>,
 871        cx: &mut ViewContext<Self>,
 872    ) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>> {
 873        let fs = self.fs.clone();
 874
 875        // Sort the paths to ensure we add worktrees for parents before their children.
 876        abs_paths.sort_unstable();
 877        cx.spawn(|this, mut cx| async move {
 878            let mut entries = Vec::new();
 879            for path in &abs_paths {
 880                entries.push(
 881                    this.update(&mut cx, |this, cx| this.project_path_for_path(path, cx))
 882                        .await
 883                        .ok(),
 884                );
 885            }
 886
 887            let tasks = abs_paths
 888                .iter()
 889                .cloned()
 890                .zip(entries.into_iter())
 891                .map(|(abs_path, project_path)| {
 892                    let this = this.clone();
 893                    cx.spawn(|mut cx| {
 894                        let fs = fs.clone();
 895                        async move {
 896                            let project_path = project_path?;
 897                            if fs.is_file(&abs_path).await {
 898                                Some(
 899                                    this.update(&mut cx, |this, cx| {
 900                                        this.open_path(project_path, true, cx)
 901                                    })
 902                                    .await,
 903                                )
 904                            } else {
 905                                None
 906                            }
 907                        }
 908                    })
 909                })
 910                .collect::<Vec<_>>();
 911
 912            futures::future::join_all(tasks).await
 913        })
 914    }
 915
 916    fn project_path_for_path(
 917        &self,
 918        abs_path: &Path,
 919        cx: &mut ViewContext<Self>,
 920    ) -> Task<Result<ProjectPath>> {
 921        let entry = self.project().update(cx, |project, cx| {
 922            project.find_or_create_local_worktree(abs_path, true, cx)
 923        });
 924        cx.spawn(|_, cx| async move {
 925            let (worktree, path) = entry.await?;
 926            Ok(ProjectPath {
 927                worktree_id: worktree.read_with(&cx, |t, _| t.id()),
 928                path: path.into(),
 929            })
 930        })
 931    }
 932
 933    /// Returns the modal that was toggled closed if it was open.
 934    pub fn toggle_modal<V, F>(
 935        &mut self,
 936        cx: &mut ViewContext<Self>,
 937        add_view: F,
 938    ) -> Option<ViewHandle<V>>
 939    where
 940        V: 'static + View,
 941        F: FnOnce(&mut Self, &mut ViewContext<Self>) -> ViewHandle<V>,
 942    {
 943        cx.notify();
 944        // Whatever modal was visible is getting clobbered. If its the same type as V, then return
 945        // it. Otherwise, create a new modal and set it as active.
 946        let already_open_modal = self.modal.take().and_then(|modal| modal.downcast::<V>());
 947        if let Some(already_open_modal) = already_open_modal {
 948            cx.focus_self();
 949            Some(already_open_modal)
 950        } else {
 951            let modal = add_view(self, cx);
 952            cx.focus(&modal);
 953            self.modal = Some(modal.into());
 954            None
 955        }
 956    }
 957
 958    pub fn modal(&self) -> Option<&AnyViewHandle> {
 959        self.modal.as_ref()
 960    }
 961
 962    pub fn dismiss_modal(&mut self, cx: &mut ViewContext<Self>) {
 963        if self.modal.take().is_some() {
 964            cx.focus(&self.active_pane);
 965            cx.notify();
 966        }
 967    }
 968
 969    pub fn show_notification<V: Notification>(
 970        &mut self,
 971        id: usize,
 972        cx: &mut ViewContext<Self>,
 973        build_notification: impl FnOnce(&mut ViewContext<Self>) -> ViewHandle<V>,
 974    ) {
 975        let type_id = TypeId::of::<V>();
 976        if self
 977            .notifications
 978            .iter()
 979            .all(|(existing_type_id, existing_id, _)| {
 980                (*existing_type_id, *existing_id) != (type_id, id)
 981            })
 982        {
 983            let notification = build_notification(cx);
 984            cx.subscribe(&notification, move |this, handle, event, cx| {
 985                if handle.read(cx).should_dismiss_notification_on_event(event) {
 986                    this.dismiss_notification(type_id, id, cx);
 987                }
 988            })
 989            .detach();
 990            self.notifications
 991                .push((type_id, id, Box::new(notification)));
 992            cx.notify();
 993        }
 994    }
 995
 996    fn dismiss_notification(&mut self, type_id: TypeId, id: usize, cx: &mut ViewContext<Self>) {
 997        self.notifications
 998            .retain(|(existing_type_id, existing_id, _)| {
 999                if (*existing_type_id, *existing_id) == (type_id, id) {
1000                    cx.notify();
1001                    false
1002                } else {
1003                    true
1004                }
1005            });
1006    }
1007
1008    pub fn items<'a>(
1009        &'a self,
1010        cx: &'a AppContext,
1011    ) -> impl 'a + Iterator<Item = &Box<dyn ItemHandle>> {
1012        self.panes.iter().flat_map(|pane| pane.read(cx).items())
1013    }
1014
1015    pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
1016        self.items_of_type(cx).max_by_key(|item| item.id())
1017    }
1018
1019    pub fn items_of_type<'a, T: Item>(
1020        &'a self,
1021        cx: &'a AppContext,
1022    ) -> impl 'a + Iterator<Item = ViewHandle<T>> {
1023        self.panes
1024            .iter()
1025            .flat_map(|pane| pane.read(cx).items_of_type())
1026    }
1027
1028    pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
1029        self.active_pane().read(cx).active_item()
1030    }
1031
1032    fn active_project_path(&self, cx: &ViewContext<Self>) -> Option<ProjectPath> {
1033        self.active_item(cx).and_then(|item| item.project_path(cx))
1034    }
1035
1036    pub fn save_active_item(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
1037        let project = self.project.clone();
1038        if let Some(item) = self.active_item(cx) {
1039            if item.can_save(cx) {
1040                if item.has_conflict(cx.as_ref()) {
1041                    const CONFLICT_MESSAGE: &'static str = "This file has changed on disk since you started editing it. Do you want to overwrite it?";
1042
1043                    let mut answer = cx.prompt(
1044                        PromptLevel::Warning,
1045                        CONFLICT_MESSAGE,
1046                        &["Overwrite", "Cancel"],
1047                    );
1048                    cx.spawn(|_, mut cx| async move {
1049                        let answer = answer.recv().await;
1050                        if answer == Some(0) {
1051                            cx.update(|cx| item.save(project, cx)).await?;
1052                        }
1053                        Ok(())
1054                    })
1055                } else {
1056                    item.save(project, cx)
1057                }
1058            } else if item.can_save_as(cx) {
1059                let worktree = self.worktrees(cx).next();
1060                let start_abs_path = worktree
1061                    .and_then(|w| w.read(cx).as_local())
1062                    .map_or(Path::new(""), |w| w.abs_path())
1063                    .to_path_buf();
1064                let mut abs_path = cx.prompt_for_new_path(&start_abs_path);
1065                cx.spawn(|_, mut cx| async move {
1066                    if let Some(abs_path) = abs_path.recv().await.flatten() {
1067                        cx.update(|cx| item.save_as(project, abs_path, cx)).await?;
1068                    }
1069                    Ok(())
1070                })
1071            } else {
1072                Task::ready(Ok(()))
1073            }
1074        } else {
1075            Task::ready(Ok(()))
1076        }
1077    }
1078
1079    pub fn toggle_sidebar_item(&mut self, action: &ToggleSidebarItem, cx: &mut ViewContext<Self>) {
1080        let sidebar = match action.side {
1081            Side::Left => &mut self.left_sidebar,
1082            Side::Right => &mut self.right_sidebar,
1083        };
1084        let active_item = sidebar.update(cx, |sidebar, cx| {
1085            sidebar.toggle_item(action.item_index, cx);
1086            sidebar.active_item().map(|item| item.to_any())
1087        });
1088        if let Some(active_item) = active_item {
1089            cx.focus(active_item);
1090        } else {
1091            cx.focus_self();
1092        }
1093        cx.notify();
1094    }
1095
1096    pub fn toggle_sidebar_item_focus(
1097        &mut self,
1098        action: &ToggleSidebarItemFocus,
1099        cx: &mut ViewContext<Self>,
1100    ) {
1101        let sidebar = match action.side {
1102            Side::Left => &mut self.left_sidebar,
1103            Side::Right => &mut self.right_sidebar,
1104        };
1105        let active_item = sidebar.update(cx, |sidebar, cx| {
1106            sidebar.activate_item(action.item_index, cx);
1107            sidebar.active_item().cloned()
1108        });
1109        if let Some(active_item) = active_item {
1110            if active_item.is_focused(cx) {
1111                cx.focus_self();
1112            } else {
1113                cx.focus(active_item.to_any());
1114            }
1115        }
1116        cx.notify();
1117    }
1118
1119    fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
1120        let pane = cx.add_view(|cx| Pane::new(cx));
1121        let pane_id = pane.id();
1122        cx.observe(&pane, move |me, _, cx| {
1123            let active_entry = me.active_project_path(cx);
1124            me.project
1125                .update(cx, |project, cx| project.set_active_path(active_entry, cx));
1126        })
1127        .detach();
1128        cx.subscribe(&pane, move |me, _, event, cx| {
1129            me.handle_pane_event(pane_id, event, cx)
1130        })
1131        .detach();
1132        self.panes.push(pane.clone());
1133        self.activate_pane(pane.clone(), cx);
1134        cx.emit(Event::PaneAdded(pane.clone()));
1135        pane
1136    }
1137
1138    pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
1139        let pane = self.active_pane().clone();
1140        Pane::add_item(self, pane, item, true, true, cx);
1141    }
1142
1143    pub fn open_path(
1144        &mut self,
1145        path: impl Into<ProjectPath>,
1146        focus_item: bool,
1147        cx: &mut ViewContext<Self>,
1148    ) -> Task<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>> {
1149        let pane = self.active_pane().downgrade();
1150        let task = self.load_path(path.into(), cx);
1151        cx.spawn(|this, mut cx| async move {
1152            let (project_entry_id, build_item) = task.await?;
1153            let pane = pane
1154                .upgrade(&cx)
1155                .ok_or_else(|| anyhow!("pane was closed"))?;
1156            this.update(&mut cx, |this, cx| {
1157                Ok(Pane::open_item(
1158                    this,
1159                    pane,
1160                    project_entry_id,
1161                    focus_item,
1162                    cx,
1163                    build_item,
1164                ))
1165            })
1166        })
1167    }
1168
1169    pub(crate) fn load_path(
1170        &mut self,
1171        path: ProjectPath,
1172        cx: &mut ViewContext<Self>,
1173    ) -> Task<
1174        Result<(
1175            ProjectEntryId,
1176            impl 'static + FnOnce(&mut MutableAppContext) -> Box<dyn ItemHandle>,
1177        )>,
1178    > {
1179        let project = self.project().clone();
1180        let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
1181        let window_id = cx.window_id();
1182        cx.as_mut().spawn(|mut cx| async move {
1183            let (project_entry_id, project_item) = project_item.await?;
1184            let build_item = cx.update(|cx| {
1185                cx.default_global::<ProjectItemBuilders>()
1186                    .get(&project_item.model_type())
1187                    .ok_or_else(|| anyhow!("no item builder for project item"))
1188                    .cloned()
1189            })?;
1190            let build_item =
1191                move |cx: &mut MutableAppContext| build_item(window_id, project, project_item, cx);
1192            Ok((project_entry_id, build_item))
1193        })
1194    }
1195
1196    pub fn open_project_item<T>(
1197        &mut self,
1198        project_item: ModelHandle<T::Item>,
1199        cx: &mut ViewContext<Self>,
1200    ) -> ViewHandle<T>
1201    where
1202        T: ProjectItem,
1203    {
1204        use project::Item as _;
1205
1206        let entry_id = project_item.read(cx).entry_id(cx);
1207        if let Some(item) = entry_id
1208            .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
1209            .and_then(|item| item.downcast())
1210        {
1211            self.activate_item(&item, cx);
1212            return item;
1213        }
1214
1215        let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
1216        self.add_item(Box::new(item.clone()), cx);
1217        item
1218    }
1219
1220    pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
1221        let result = self.panes.iter().find_map(|pane| {
1222            if let Some(ix) = pane.read(cx).index_for_item(item) {
1223                Some((pane.clone(), ix))
1224            } else {
1225                None
1226            }
1227        });
1228        if let Some((pane, ix)) = result {
1229            self.activate_pane(pane.clone(), cx);
1230            pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, cx));
1231            true
1232        } else {
1233            false
1234        }
1235    }
1236
1237    pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
1238        let next_pane = {
1239            let panes = self.center.panes();
1240            let ix = panes
1241                .iter()
1242                .position(|pane| **pane == self.active_pane)
1243                .unwrap();
1244            let next_ix = (ix + 1) % panes.len();
1245            panes[next_ix].clone()
1246        };
1247        self.activate_pane(next_pane, cx);
1248    }
1249
1250    pub fn activate_previous_pane(&mut self, cx: &mut ViewContext<Self>) {
1251        let prev_pane = {
1252            let panes = self.center.panes();
1253            let ix = panes
1254                .iter()
1255                .position(|pane| **pane == self.active_pane)
1256                .unwrap();
1257            let prev_ix = if ix == 0 { panes.len() - 1 } else { ix - 1 };
1258            panes[prev_ix].clone()
1259        };
1260        self.activate_pane(prev_pane, cx);
1261    }
1262
1263    fn activate_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
1264        if self.active_pane != pane {
1265            self.active_pane = pane.clone();
1266            self.status_bar.update(cx, |status_bar, cx| {
1267                status_bar.set_active_pane(&self.active_pane, cx);
1268            });
1269            cx.focus(&self.active_pane);
1270            cx.notify();
1271        }
1272
1273        self.update_followers(
1274            proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView {
1275                id: self.active_item(cx).map(|item| item.id() as u64),
1276                leader_id: self.leader_for_pane(&pane).map(|id| id.0),
1277            }),
1278            cx,
1279        );
1280    }
1281
1282    fn handle_pane_event(
1283        &mut self,
1284        pane_id: usize,
1285        event: &pane::Event,
1286        cx: &mut ViewContext<Self>,
1287    ) {
1288        if let Some(pane) = self.pane(pane_id) {
1289            match event {
1290                pane::Event::Split(direction) => {
1291                    self.split_pane(pane, *direction, cx);
1292                }
1293                pane::Event::Remove => {
1294                    self.remove_pane(pane, cx);
1295                }
1296                pane::Event::Activate => {
1297                    self.activate_pane(pane, cx);
1298                }
1299                pane::Event::ActivateItem { local } => {
1300                    if *local {
1301                        self.unfollow(&pane, cx);
1302                    }
1303                }
1304            }
1305        } else {
1306            error!("pane {} not found", pane_id);
1307        }
1308    }
1309
1310    pub fn split_pane(
1311        &mut self,
1312        pane: ViewHandle<Pane>,
1313        direction: SplitDirection,
1314        cx: &mut ViewContext<Self>,
1315    ) -> ViewHandle<Pane> {
1316        let new_pane = self.add_pane(cx);
1317        self.activate_pane(new_pane.clone(), cx);
1318        if let Some(item) = pane.read(cx).active_item() {
1319            if let Some(clone) = item.clone_on_split(cx.as_mut()) {
1320                Pane::add_item(self, new_pane.clone(), clone, true, true, cx);
1321            }
1322        }
1323        self.center.split(&pane, &new_pane, direction).unwrap();
1324        cx.notify();
1325        new_pane
1326    }
1327
1328    fn remove_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
1329        if self.center.remove(&pane).unwrap() {
1330            self.panes.retain(|p| p != &pane);
1331            self.activate_pane(self.panes.last().unwrap().clone(), cx);
1332            self.unfollow(&pane, cx);
1333            self.last_leaders_by_pane.remove(&pane.downgrade());
1334            cx.notify();
1335        }
1336    }
1337
1338    pub fn panes(&self) -> &[ViewHandle<Pane>] {
1339        &self.panes
1340    }
1341
1342    fn pane(&self, pane_id: usize) -> Option<ViewHandle<Pane>> {
1343        self.panes.iter().find(|pane| pane.id() == pane_id).cloned()
1344    }
1345
1346    pub fn active_pane(&self) -> &ViewHandle<Pane> {
1347        &self.active_pane
1348    }
1349
1350    fn project_remote_id_changed(&mut self, remote_id: Option<u64>, cx: &mut ViewContext<Self>) {
1351        if let Some(remote_id) = remote_id {
1352            self.remote_entity_subscription =
1353                Some(self.client.add_view_for_remote_entity(remote_id, cx));
1354        } else {
1355            self.remote_entity_subscription.take();
1356        }
1357    }
1358
1359    fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
1360        self.leader_state.followers.remove(&peer_id);
1361        if let Some(states_by_pane) = self.follower_states_by_leader.remove(&peer_id) {
1362            for state in states_by_pane.into_values() {
1363                for item in state.items_by_leader_view_id.into_values() {
1364                    if let FollowerItem::Loaded(item) = item {
1365                        item.set_leader_replica_id(None, cx);
1366                    }
1367                }
1368            }
1369        }
1370        cx.notify();
1371    }
1372
1373    pub fn toggle_follow(
1374        &mut self,
1375        ToggleFollow(leader_id): &ToggleFollow,
1376        cx: &mut ViewContext<Self>,
1377    ) -> Option<Task<Result<()>>> {
1378        let leader_id = *leader_id;
1379        let pane = self.active_pane().clone();
1380
1381        if let Some(prev_leader_id) = self.unfollow(&pane, cx) {
1382            if leader_id == prev_leader_id {
1383                return None;
1384            }
1385        }
1386
1387        self.last_leaders_by_pane
1388            .insert(pane.downgrade(), leader_id);
1389        self.follower_states_by_leader
1390            .entry(leader_id)
1391            .or_default()
1392            .insert(pane.clone(), Default::default());
1393        cx.notify();
1394
1395        let project_id = self.project.read(cx).remote_id()?;
1396        let request = self.client.request(proto::Follow {
1397            project_id,
1398            leader_id: leader_id.0,
1399        });
1400        Some(cx.spawn_weak(|this, mut cx| async move {
1401            let response = request.await?;
1402            if let Some(this) = this.upgrade(&cx) {
1403                this.update(&mut cx, |this, _| {
1404                    let state = this
1405                        .follower_states_by_leader
1406                        .get_mut(&leader_id)
1407                        .and_then(|states_by_pane| states_by_pane.get_mut(&pane))
1408                        .ok_or_else(|| anyhow!("following interrupted"))?;
1409                    state.active_view_id = response.active_view_id;
1410                    Ok::<_, anyhow::Error>(())
1411                })?;
1412                Self::add_views_from_leader(this, leader_id, vec![pane], response.views, &mut cx)
1413                    .await?;
1414            }
1415            Ok(())
1416        }))
1417    }
1418
1419    pub fn follow_next_collaborator(
1420        &mut self,
1421        _: &FollowNextCollaborator,
1422        cx: &mut ViewContext<Self>,
1423    ) -> Option<Task<Result<()>>> {
1424        let collaborators = self.project.read(cx).collaborators();
1425        let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
1426            let mut collaborators = collaborators.keys().copied();
1427            while let Some(peer_id) = collaborators.next() {
1428                if peer_id == leader_id {
1429                    break;
1430                }
1431            }
1432            collaborators.next()
1433        } else if let Some(last_leader_id) =
1434            self.last_leaders_by_pane.get(&self.active_pane.downgrade())
1435        {
1436            if collaborators.contains_key(last_leader_id) {
1437                Some(*last_leader_id)
1438            } else {
1439                None
1440            }
1441        } else {
1442            None
1443        };
1444
1445        next_leader_id
1446            .or_else(|| collaborators.keys().copied().next())
1447            .and_then(|leader_id| self.toggle_follow(&ToggleFollow(leader_id), cx))
1448    }
1449
1450    pub fn unfollow(
1451        &mut self,
1452        pane: &ViewHandle<Pane>,
1453        cx: &mut ViewContext<Self>,
1454    ) -> Option<PeerId> {
1455        for (leader_id, states_by_pane) in &mut self.follower_states_by_leader {
1456            let leader_id = *leader_id;
1457            if let Some(state) = states_by_pane.remove(&pane) {
1458                for (_, item) in state.items_by_leader_view_id {
1459                    if let FollowerItem::Loaded(item) = item {
1460                        item.set_leader_replica_id(None, cx);
1461                    }
1462                }
1463
1464                if states_by_pane.is_empty() {
1465                    self.follower_states_by_leader.remove(&leader_id);
1466                    if let Some(project_id) = self.project.read(cx).remote_id() {
1467                        self.client
1468                            .send(proto::Unfollow {
1469                                project_id,
1470                                leader_id: leader_id.0,
1471                            })
1472                            .log_err();
1473                    }
1474                }
1475
1476                cx.notify();
1477                return Some(leader_id);
1478            }
1479        }
1480        None
1481    }
1482
1483    fn render_connection_status(&self, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
1484        let theme = &cx.global::<Settings>().theme;
1485        match &*self.client.status().borrow() {
1486            client::Status::ConnectionError
1487            | client::Status::ConnectionLost
1488            | client::Status::Reauthenticating
1489            | client::Status::Reconnecting { .. }
1490            | client::Status::ReconnectionError { .. } => Some(
1491                Container::new(
1492                    Align::new(
1493                        ConstrainedBox::new(
1494                            Svg::new("icons/offline-14.svg")
1495                                .with_color(theme.workspace.titlebar.offline_icon.color)
1496                                .boxed(),
1497                        )
1498                        .with_width(theme.workspace.titlebar.offline_icon.width)
1499                        .boxed(),
1500                    )
1501                    .boxed(),
1502                )
1503                .with_style(theme.workspace.titlebar.offline_icon.container)
1504                .boxed(),
1505            ),
1506            client::Status::UpgradeRequired => Some(
1507                Label::new(
1508                    "Please update Zed to collaborate".to_string(),
1509                    theme.workspace.titlebar.outdated_warning.text.clone(),
1510                )
1511                .contained()
1512                .with_style(theme.workspace.titlebar.outdated_warning.container)
1513                .aligned()
1514                .boxed(),
1515            ),
1516            _ => None,
1517        }
1518    }
1519
1520    fn render_titlebar(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
1521        let mut worktree_root_names = String::new();
1522        {
1523            let mut worktrees = self.project.read(cx).visible_worktrees(cx).peekable();
1524            while let Some(worktree) = worktrees.next() {
1525                worktree_root_names.push_str(worktree.read(cx).root_name());
1526                if worktrees.peek().is_some() {
1527                    worktree_root_names.push_str(", ");
1528                }
1529            }
1530        }
1531
1532        ConstrainedBox::new(
1533            Container::new(
1534                Stack::new()
1535                    .with_child(
1536                        Label::new(worktree_root_names, theme.workspace.titlebar.title.clone())
1537                            .aligned()
1538                            .left()
1539                            .boxed(),
1540                    )
1541                    .with_child(
1542                        Align::new(
1543                            Flex::row()
1544                                .with_children(self.render_collaborators(theme, cx))
1545                                .with_children(self.render_current_user(
1546                                    self.user_store.read(cx).current_user().as_ref(),
1547                                    self.project.read(cx).replica_id(),
1548                                    theme,
1549                                    cx,
1550                                ))
1551                                .with_children(self.render_connection_status(cx))
1552                                .boxed(),
1553                        )
1554                        .right()
1555                        .boxed(),
1556                    )
1557                    .boxed(),
1558            )
1559            .with_style(theme.workspace.titlebar.container)
1560            .boxed(),
1561        )
1562        .with_height(theme.workspace.titlebar.height)
1563        .named("titlebar")
1564    }
1565
1566    fn render_collaborators(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> Vec<ElementBox> {
1567        let mut collaborators = self
1568            .project
1569            .read(cx)
1570            .collaborators()
1571            .values()
1572            .cloned()
1573            .collect::<Vec<_>>();
1574        collaborators.sort_unstable_by_key(|collaborator| collaborator.replica_id);
1575        collaborators
1576            .into_iter()
1577            .filter_map(|collaborator| {
1578                Some(self.render_avatar(
1579                    collaborator.user.avatar.clone()?,
1580                    collaborator.replica_id,
1581                    Some(collaborator.peer_id),
1582                    theme,
1583                    cx,
1584                ))
1585            })
1586            .collect()
1587    }
1588
1589    fn render_current_user(
1590        &self,
1591        user: Option<&Arc<User>>,
1592        replica_id: ReplicaId,
1593        theme: &Theme,
1594        cx: &mut RenderContext<Self>,
1595    ) -> Option<ElementBox> {
1596        let status = *self.client.status().borrow();
1597        if let Some(avatar) = user.and_then(|user| user.avatar.clone()) {
1598            Some(self.render_avatar(avatar, replica_id, None, theme, cx))
1599        } else if matches!(status, client::Status::UpgradeRequired) {
1600            None
1601        } else {
1602            Some(
1603                MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
1604                    let style = theme
1605                        .workspace
1606                        .titlebar
1607                        .sign_in_prompt
1608                        .style_for(state, false);
1609                    Label::new("Sign in".to_string(), style.text.clone())
1610                        .contained()
1611                        .with_style(style.container)
1612                        .boxed()
1613                })
1614                .on_click(|_, cx| cx.dispatch_action(Authenticate))
1615                .with_cursor_style(CursorStyle::PointingHand)
1616                .aligned()
1617                .boxed(),
1618            )
1619        }
1620    }
1621
1622    fn render_avatar(
1623        &self,
1624        avatar: Arc<ImageData>,
1625        replica_id: ReplicaId,
1626        peer_id: Option<PeerId>,
1627        theme: &Theme,
1628        cx: &mut RenderContext<Self>,
1629    ) -> ElementBox {
1630        let replica_color = theme.editor.replica_selection_style(replica_id).cursor;
1631        let is_followed = peer_id.map_or(false, |peer_id| {
1632            self.follower_states_by_leader.contains_key(&peer_id)
1633        });
1634        let mut avatar_style = theme.workspace.titlebar.avatar;
1635        if is_followed {
1636            avatar_style.border = Border::all(1.0, replica_color);
1637        }
1638        let content = Stack::new()
1639            .with_child(
1640                Image::new(avatar)
1641                    .with_style(avatar_style)
1642                    .constrained()
1643                    .with_width(theme.workspace.titlebar.avatar_width)
1644                    .aligned()
1645                    .boxed(),
1646            )
1647            .with_child(
1648                AvatarRibbon::new(replica_color)
1649                    .constrained()
1650                    .with_width(theme.workspace.titlebar.avatar_ribbon.width)
1651                    .with_height(theme.workspace.titlebar.avatar_ribbon.height)
1652                    .aligned()
1653                    .bottom()
1654                    .boxed(),
1655            )
1656            .constrained()
1657            .with_width(theme.workspace.titlebar.avatar_width)
1658            .contained()
1659            .with_margin_left(theme.workspace.titlebar.avatar_margin)
1660            .boxed();
1661
1662        if let Some(peer_id) = peer_id {
1663            MouseEventHandler::new::<ToggleFollow, _, _>(replica_id.into(), cx, move |_, _| content)
1664                .with_cursor_style(CursorStyle::PointingHand)
1665                .on_click(move |_, cx| cx.dispatch_action(ToggleFollow(peer_id)))
1666                .boxed()
1667        } else {
1668            content
1669        }
1670    }
1671
1672    fn render_disconnected_overlay(&self, cx: &AppContext) -> Option<ElementBox> {
1673        if self.project.read(cx).is_read_only() {
1674            let theme = &cx.global::<Settings>().theme;
1675            Some(
1676                EventHandler::new(
1677                    Label::new(
1678                        "Your connection to the remote project has been lost.".to_string(),
1679                        theme.workspace.disconnected_overlay.text.clone(),
1680                    )
1681                    .aligned()
1682                    .contained()
1683                    .with_style(theme.workspace.disconnected_overlay.container)
1684                    .boxed(),
1685                )
1686                .capture(|_, _, _| true)
1687                .boxed(),
1688            )
1689        } else {
1690            None
1691        }
1692    }
1693
1694    fn render_notifications(&self, theme: &theme::Workspace) -> Option<ElementBox> {
1695        if self.notifications.is_empty() {
1696            None
1697        } else {
1698            Some(
1699                Flex::column()
1700                    .with_children(self.notifications.iter().map(|(_, _, notification)| {
1701                        ChildView::new(notification.as_ref())
1702                            .contained()
1703                            .with_style(theme.notification)
1704                            .boxed()
1705                    }))
1706                    .constrained()
1707                    .with_width(theme.notifications.width)
1708                    .contained()
1709                    .with_style(theme.notifications.container)
1710                    .aligned()
1711                    .bottom()
1712                    .right()
1713                    .boxed(),
1714            )
1715        }
1716    }
1717
1718    // RPC handlers
1719
1720    async fn handle_follow(
1721        this: ViewHandle<Self>,
1722        envelope: TypedEnvelope<proto::Follow>,
1723        _: Arc<Client>,
1724        mut cx: AsyncAppContext,
1725    ) -> Result<proto::FollowResponse> {
1726        this.update(&mut cx, |this, cx| {
1727            this.leader_state
1728                .followers
1729                .insert(envelope.original_sender_id()?);
1730
1731            let active_view_id = this
1732                .active_item(cx)
1733                .and_then(|i| i.to_followable_item_handle(cx))
1734                .map(|i| i.id() as u64);
1735            Ok(proto::FollowResponse {
1736                active_view_id,
1737                views: this
1738                    .panes()
1739                    .iter()
1740                    .flat_map(|pane| {
1741                        let leader_id = this.leader_for_pane(pane).map(|id| id.0);
1742                        pane.read(cx).items().filter_map({
1743                            let cx = &cx;
1744                            move |item| {
1745                                let id = item.id() as u64;
1746                                let item = item.to_followable_item_handle(cx)?;
1747                                let variant = item.to_state_proto(cx)?;
1748                                Some(proto::View {
1749                                    id,
1750                                    leader_id,
1751                                    variant: Some(variant),
1752                                })
1753                            }
1754                        })
1755                    })
1756                    .collect(),
1757            })
1758        })
1759    }
1760
1761    async fn handle_unfollow(
1762        this: ViewHandle<Self>,
1763        envelope: TypedEnvelope<proto::Unfollow>,
1764        _: Arc<Client>,
1765        mut cx: AsyncAppContext,
1766    ) -> Result<()> {
1767        this.update(&mut cx, |this, _| {
1768            this.leader_state
1769                .followers
1770                .remove(&envelope.original_sender_id()?);
1771            Ok(())
1772        })
1773    }
1774
1775    async fn handle_update_followers(
1776        this: ViewHandle<Self>,
1777        envelope: TypedEnvelope<proto::UpdateFollowers>,
1778        _: Arc<Client>,
1779        mut cx: AsyncAppContext,
1780    ) -> Result<()> {
1781        let leader_id = envelope.original_sender_id()?;
1782        match envelope
1783            .payload
1784            .variant
1785            .ok_or_else(|| anyhow!("invalid update"))?
1786        {
1787            proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
1788                this.update(&mut cx, |this, cx| {
1789                    this.update_leader_state(leader_id, cx, |state, _| {
1790                        state.active_view_id = update_active_view.id;
1791                    });
1792                    Ok::<_, anyhow::Error>(())
1793                })
1794            }
1795            proto::update_followers::Variant::UpdateView(update_view) => {
1796                this.update(&mut cx, |this, cx| {
1797                    let variant = update_view
1798                        .variant
1799                        .ok_or_else(|| anyhow!("missing update view variant"))?;
1800                    this.update_leader_state(leader_id, cx, |state, cx| {
1801                        let variant = variant.clone();
1802                        match state
1803                            .items_by_leader_view_id
1804                            .entry(update_view.id)
1805                            .or_insert(FollowerItem::Loading(Vec::new()))
1806                        {
1807                            FollowerItem::Loaded(item) => {
1808                                item.apply_update_proto(variant, cx).log_err();
1809                            }
1810                            FollowerItem::Loading(updates) => updates.push(variant),
1811                        }
1812                    });
1813                    Ok(())
1814                })
1815            }
1816            proto::update_followers::Variant::CreateView(view) => {
1817                let panes = this.read_with(&cx, |this, _| {
1818                    this.follower_states_by_leader
1819                        .get(&leader_id)
1820                        .into_iter()
1821                        .flat_map(|states_by_pane| states_by_pane.keys())
1822                        .cloned()
1823                        .collect()
1824                });
1825                Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], &mut cx)
1826                    .await?;
1827                Ok(())
1828            }
1829        }
1830        .log_err();
1831
1832        Ok(())
1833    }
1834
1835    async fn add_views_from_leader(
1836        this: ViewHandle<Self>,
1837        leader_id: PeerId,
1838        panes: Vec<ViewHandle<Pane>>,
1839        views: Vec<proto::View>,
1840        cx: &mut AsyncAppContext,
1841    ) -> Result<()> {
1842        let project = this.read_with(cx, |this, _| this.project.clone());
1843        let replica_id = project
1844            .read_with(cx, |project, _| {
1845                project
1846                    .collaborators()
1847                    .get(&leader_id)
1848                    .map(|c| c.replica_id)
1849            })
1850            .ok_or_else(|| anyhow!("no such collaborator {}", leader_id))?;
1851
1852        let item_builders = cx.update(|cx| {
1853            cx.default_global::<FollowableItemBuilders>()
1854                .values()
1855                .map(|b| b.0)
1856                .collect::<Vec<_>>()
1857                .clone()
1858        });
1859
1860        let mut item_tasks_by_pane = HashMap::default();
1861        for pane in panes {
1862            let mut item_tasks = Vec::new();
1863            let mut leader_view_ids = Vec::new();
1864            for view in &views {
1865                let mut variant = view.variant.clone();
1866                if variant.is_none() {
1867                    Err(anyhow!("missing variant"))?;
1868                }
1869                for build_item in &item_builders {
1870                    let task =
1871                        cx.update(|cx| build_item(pane.clone(), project.clone(), &mut variant, cx));
1872                    if let Some(task) = task {
1873                        item_tasks.push(task);
1874                        leader_view_ids.push(view.id);
1875                        break;
1876                    } else {
1877                        assert!(variant.is_some());
1878                    }
1879                }
1880            }
1881
1882            item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids));
1883        }
1884
1885        for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane {
1886            let items = futures::future::try_join_all(item_tasks).await?;
1887            this.update(cx, |this, cx| {
1888                let state = this
1889                    .follower_states_by_leader
1890                    .get_mut(&leader_id)?
1891                    .get_mut(&pane)?;
1892
1893                for (id, item) in leader_view_ids.into_iter().zip(items) {
1894                    item.set_leader_replica_id(Some(replica_id), cx);
1895                    match state.items_by_leader_view_id.entry(id) {
1896                        hash_map::Entry::Occupied(e) => {
1897                            let e = e.into_mut();
1898                            if let FollowerItem::Loading(updates) = e {
1899                                for update in updates.drain(..) {
1900                                    item.apply_update_proto(update, cx)
1901                                        .context("failed to apply view update")
1902                                        .log_err();
1903                                }
1904                            }
1905                            *e = FollowerItem::Loaded(item);
1906                        }
1907                        hash_map::Entry::Vacant(e) => {
1908                            e.insert(FollowerItem::Loaded(item));
1909                        }
1910                    }
1911                }
1912
1913                Some(())
1914            });
1915        }
1916        this.update(cx, |this, cx| this.leader_updated(leader_id, cx));
1917
1918        Ok(())
1919    }
1920
1921    fn update_followers(
1922        &self,
1923        update: proto::update_followers::Variant,
1924        cx: &AppContext,
1925    ) -> Option<()> {
1926        let project_id = self.project.read(cx).remote_id()?;
1927        if !self.leader_state.followers.is_empty() {
1928            self.client
1929                .send(proto::UpdateFollowers {
1930                    project_id,
1931                    follower_ids: self.leader_state.followers.iter().map(|f| f.0).collect(),
1932                    variant: Some(update),
1933                })
1934                .log_err();
1935        }
1936        None
1937    }
1938
1939    pub fn leader_for_pane(&self, pane: &ViewHandle<Pane>) -> Option<PeerId> {
1940        self.follower_states_by_leader
1941            .iter()
1942            .find_map(|(leader_id, state)| {
1943                if state.contains_key(pane) {
1944                    Some(*leader_id)
1945                } else {
1946                    None
1947                }
1948            })
1949    }
1950
1951    fn update_leader_state(
1952        &mut self,
1953        leader_id: PeerId,
1954        cx: &mut ViewContext<Self>,
1955        mut update_fn: impl FnMut(&mut FollowerState, &mut ViewContext<Self>),
1956    ) {
1957        for (_, state) in self
1958            .follower_states_by_leader
1959            .get_mut(&leader_id)
1960            .into_iter()
1961            .flatten()
1962        {
1963            update_fn(state, cx);
1964        }
1965        self.leader_updated(leader_id, cx);
1966    }
1967
1968    fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
1969        let mut items_to_add = Vec::new();
1970        for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
1971            if let Some(active_item) = state
1972                .active_view_id
1973                .and_then(|id| state.items_by_leader_view_id.get(&id))
1974            {
1975                if let FollowerItem::Loaded(item) = active_item {
1976                    items_to_add.push((pane.clone(), item.boxed_clone()));
1977                }
1978            }
1979        }
1980
1981        for (pane, item) in items_to_add {
1982            Pane::add_item(self, pane.clone(), item.boxed_clone(), false, false, cx);
1983            if pane == self.active_pane {
1984                pane.update(cx, |pane, cx| pane.focus_active_item(cx));
1985            }
1986            cx.notify();
1987        }
1988        None
1989    }
1990}
1991
1992impl Entity for Workspace {
1993    type Event = Event;
1994}
1995
1996impl View for Workspace {
1997    fn ui_name() -> &'static str {
1998        "Workspace"
1999    }
2000
2001    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
2002        let theme = cx.global::<Settings>().theme.clone();
2003        Stack::new()
2004            .with_child(
2005                Flex::column()
2006                    .with_child(self.render_titlebar(&theme, cx))
2007                    .with_child(
2008                        Stack::new()
2009                            .with_child({
2010                                Flex::row()
2011                                    .with_children(
2012                                        if self.left_sidebar.read(cx).active_item().is_some() {
2013                                            Some(
2014                                                ChildView::new(&self.left_sidebar)
2015                                                    .flex(0.8, false)
2016                                                    .boxed(),
2017                                            )
2018                                        } else {
2019                                            None
2020                                        },
2021                                    )
2022                                    .with_child(
2023                                        FlexItem::new(self.center.render(
2024                                            &theme,
2025                                            &self.follower_states_by_leader,
2026                                            self.project.read(cx).collaborators(),
2027                                        ))
2028                                        .flex(1., true)
2029                                        .boxed(),
2030                                    )
2031                                    .with_children(
2032                                        if self.right_sidebar.read(cx).active_item().is_some() {
2033                                            Some(
2034                                                ChildView::new(&self.right_sidebar)
2035                                                    .flex(0.8, false)
2036                                                    .boxed(),
2037                                            )
2038                                        } else {
2039                                            None
2040                                        },
2041                                    )
2042                                    .boxed()
2043                            })
2044                            .with_children(self.modal.as_ref().map(|m| {
2045                                ChildView::new(m)
2046                                    .contained()
2047                                    .with_style(theme.workspace.modal)
2048                                    .aligned()
2049                                    .top()
2050                                    .boxed()
2051                            }))
2052                            .with_children(self.render_notifications(&theme.workspace))
2053                            .flex(1.0, true)
2054                            .boxed(),
2055                    )
2056                    .with_child(ChildView::new(&self.status_bar).boxed())
2057                    .contained()
2058                    .with_background_color(theme.workspace.background)
2059                    .boxed(),
2060            )
2061            .with_children(self.render_disconnected_overlay(cx))
2062            .named("workspace")
2063    }
2064
2065    fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
2066        cx.focus(&self.active_pane);
2067    }
2068}
2069
2070pub trait WorkspaceHandle {
2071    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
2072}
2073
2074impl WorkspaceHandle for ViewHandle<Workspace> {
2075    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
2076        self.read(cx)
2077            .worktrees(cx)
2078            .flat_map(|worktree| {
2079                let worktree_id = worktree.read(cx).id();
2080                worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
2081                    worktree_id,
2082                    path: f.path.clone(),
2083                })
2084            })
2085            .collect::<Vec<_>>()
2086    }
2087}
2088
2089pub struct AvatarRibbon {
2090    color: Color,
2091}
2092
2093impl AvatarRibbon {
2094    pub fn new(color: Color) -> AvatarRibbon {
2095        AvatarRibbon { color }
2096    }
2097}
2098
2099impl Element for AvatarRibbon {
2100    type LayoutState = ();
2101
2102    type PaintState = ();
2103
2104    fn layout(
2105        &mut self,
2106        constraint: gpui::SizeConstraint,
2107        _: &mut gpui::LayoutContext,
2108    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
2109        (constraint.max, ())
2110    }
2111
2112    fn paint(
2113        &mut self,
2114        bounds: gpui::geometry::rect::RectF,
2115        _: gpui::geometry::rect::RectF,
2116        _: &mut Self::LayoutState,
2117        cx: &mut gpui::PaintContext,
2118    ) -> Self::PaintState {
2119        let mut path = PathBuilder::new();
2120        path.reset(bounds.lower_left());
2121        path.curve_to(
2122            bounds.origin() + vec2f(bounds.height(), 0.),
2123            bounds.origin(),
2124        );
2125        path.line_to(bounds.upper_right() - vec2f(bounds.height(), 0.));
2126        path.curve_to(bounds.lower_right(), bounds.upper_right());
2127        path.line_to(bounds.lower_left());
2128        cx.scene.push_path(path.build(self.color, None));
2129    }
2130
2131    fn dispatch_event(
2132        &mut self,
2133        _: &gpui::Event,
2134        _: RectF,
2135        _: RectF,
2136        _: &mut Self::LayoutState,
2137        _: &mut Self::PaintState,
2138        _: &mut gpui::EventContext,
2139    ) -> bool {
2140        false
2141    }
2142
2143    fn debug(
2144        &self,
2145        bounds: gpui::geometry::rect::RectF,
2146        _: &Self::LayoutState,
2147        _: &Self::PaintState,
2148        _: &gpui::DebugContext,
2149    ) -> gpui::json::Value {
2150        json::json!({
2151            "type": "AvatarRibbon",
2152            "bounds": bounds.to_json(),
2153            "color": self.color.to_json(),
2154        })
2155    }
2156}
2157
2158impl std::fmt::Debug for OpenPaths {
2159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2160        f.debug_struct("OpenPaths")
2161            .field("paths", &self.paths)
2162            .finish()
2163    }
2164}
2165
2166fn open(_: &Open, cx: &mut MutableAppContext) {
2167    let mut paths = cx.prompt_for_paths(PathPromptOptions {
2168        files: true,
2169        directories: true,
2170        multiple: true,
2171    });
2172    cx.spawn(|mut cx| async move {
2173        if let Some(paths) = paths.recv().await.flatten() {
2174            cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths }));
2175        }
2176    })
2177    .detach();
2178}
2179
2180pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
2181
2182pub fn open_paths(
2183    abs_paths: &[PathBuf],
2184    app_state: &Arc<AppState>,
2185    cx: &mut MutableAppContext,
2186) -> Task<(
2187    ViewHandle<Workspace>,
2188    Vec<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>,
2189)> {
2190    log::info!("open paths {:?}", abs_paths);
2191
2192    // Open paths in existing workspace if possible
2193    let mut existing = None;
2194    for window_id in cx.window_ids().collect::<Vec<_>>() {
2195        if let Some(workspace_handle) = cx.root_view::<Workspace>(window_id) {
2196            if workspace_handle.update(cx, |workspace, cx| {
2197                if workspace.contains_paths(abs_paths, cx.as_ref()) {
2198                    cx.activate_window(window_id);
2199                    existing = Some(workspace_handle.clone());
2200                    true
2201                } else {
2202                    false
2203                }
2204            }) {
2205                break;
2206            }
2207        }
2208    }
2209
2210    let app_state = app_state.clone();
2211    let abs_paths = abs_paths.to_vec();
2212    cx.spawn(|mut cx| async move {
2213        let workspace = if let Some(existing) = existing {
2214            existing
2215        } else {
2216            let contains_directory =
2217                futures::future::join_all(abs_paths.iter().map(|path| app_state.fs.is_file(path)))
2218                    .await
2219                    .contains(&false);
2220
2221            cx.add_window((app_state.build_window_options)(), |cx| {
2222                let project = Project::local(
2223                    app_state.client.clone(),
2224                    app_state.user_store.clone(),
2225                    app_state.languages.clone(),
2226                    app_state.fs.clone(),
2227                    cx,
2228                );
2229                let mut workspace = (app_state.build_workspace)(project, &app_state, cx);
2230                if contains_directory {
2231                    workspace.toggle_sidebar_item(
2232                        &ToggleSidebarItem {
2233                            side: Side::Left,
2234                            item_index: 0,
2235                        },
2236                        cx,
2237                    );
2238                }
2239                workspace
2240            })
2241            .1
2242        };
2243
2244        let items = workspace
2245            .update(&mut cx, |workspace, cx| workspace.open_paths(abs_paths, cx))
2246            .await;
2247        (workspace, items)
2248    })
2249}
2250
2251pub fn join_project(
2252    contact: Arc<Contact>,
2253    project_index: usize,
2254    app_state: &Arc<AppState>,
2255    cx: &mut MutableAppContext,
2256) {
2257    let project_id = contact.projects[project_index].id;
2258
2259    for window_id in cx.window_ids().collect::<Vec<_>>() {
2260        if let Some(workspace) = cx.root_view::<Workspace>(window_id) {
2261            if workspace.read(cx).project().read(cx).remote_id() == Some(project_id) {
2262                cx.activate_window(window_id);
2263                return;
2264            }
2265        }
2266    }
2267
2268    cx.add_window((app_state.build_window_options)(), |cx| {
2269        WaitingRoom::new(contact, project_index, app_state.clone(), cx)
2270    });
2271}
2272
2273fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
2274    let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
2275        let project = Project::local(
2276            app_state.client.clone(),
2277            app_state.user_store.clone(),
2278            app_state.languages.clone(),
2279            app_state.fs.clone(),
2280            cx,
2281        );
2282        (app_state.build_workspace)(project, app_state, cx)
2283    });
2284    cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew);
2285}