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