workspace.rs

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