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!(ToggleFollow, 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::toggle_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 toggle_follow(
1215        &mut self,
1216        ToggleFollow(leader_id): &ToggleFollow,
1217        cx: &mut ViewContext<Self>,
1218    ) -> Option<Task<Result<()>>> {
1219        let leader_id = *leader_id;
1220        let pane = self.active_pane().clone();
1221
1222        if let Some(prev_leader_id) = self.unfollow(&pane, cx) {
1223            if leader_id == prev_leader_id {
1224                cx.notify();
1225                return None;
1226            }
1227        }
1228
1229        self.follower_states_by_leader
1230            .entry(leader_id)
1231            .or_default()
1232            .insert(pane.clone(), Default::default());
1233
1234        let project_id = self.project.read(cx).remote_id()?;
1235        let request = self.client.request(proto::Follow {
1236            project_id,
1237            leader_id: leader_id.0,
1238        });
1239        Some(cx.spawn_weak(|this, mut cx| async move {
1240            let response = request.await?;
1241            if let Some(this) = this.upgrade(&cx) {
1242                this.update(&mut cx, |this, _| {
1243                    let state = this
1244                        .follower_states_by_leader
1245                        .get_mut(&leader_id)
1246                        .and_then(|states_by_pane| states_by_pane.get_mut(&pane))
1247                        .ok_or_else(|| anyhow!("following interrupted"))?;
1248                    state.active_view_id = response.active_view_id;
1249                    Ok::<_, anyhow::Error>(())
1250                })?;
1251                Self::add_views_from_leader(this, leader_id, vec![pane], response.views, &mut cx)
1252                    .await?;
1253            }
1254            Ok(())
1255        }))
1256    }
1257
1258    pub fn unfollow(
1259        &mut self,
1260        pane: &ViewHandle<Pane>,
1261        cx: &mut ViewContext<Self>,
1262    ) -> Option<PeerId> {
1263        for (leader_id, states_by_pane) in &mut self.follower_states_by_leader {
1264            if let Some(state) = states_by_pane.remove(&pane) {
1265                for (_, item) in state.items_by_leader_view_id {
1266                    if let FollowerItem::Loaded(item) = item {
1267                        item.set_following(false, cx);
1268                    }
1269                }
1270
1271                if states_by_pane.is_empty() {
1272                    if let Some(project_id) = self.project.read(cx).remote_id() {
1273                        self.client
1274                            .send(proto::Unfollow {
1275                                project_id,
1276                                leader_id: leader_id.0,
1277                            })
1278                            .log_err();
1279                    }
1280                }
1281
1282                cx.notify();
1283                return Some(*leader_id);
1284            }
1285        }
1286        None
1287    }
1288
1289    fn render_connection_status(&self, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
1290        let theme = &cx.global::<Settings>().theme;
1291        match &*self.client.status().borrow() {
1292            client::Status::ConnectionError
1293            | client::Status::ConnectionLost
1294            | client::Status::Reauthenticating
1295            | client::Status::Reconnecting { .. }
1296            | client::Status::ReconnectionError { .. } => Some(
1297                Container::new(
1298                    Align::new(
1299                        ConstrainedBox::new(
1300                            Svg::new("icons/offline-14.svg")
1301                                .with_color(theme.workspace.titlebar.offline_icon.color)
1302                                .boxed(),
1303                        )
1304                        .with_width(theme.workspace.titlebar.offline_icon.width)
1305                        .boxed(),
1306                    )
1307                    .boxed(),
1308                )
1309                .with_style(theme.workspace.titlebar.offline_icon.container)
1310                .boxed(),
1311            ),
1312            client::Status::UpgradeRequired => Some(
1313                Label::new(
1314                    "Please update Zed to collaborate".to_string(),
1315                    theme.workspace.titlebar.outdated_warning.text.clone(),
1316                )
1317                .contained()
1318                .with_style(theme.workspace.titlebar.outdated_warning.container)
1319                .aligned()
1320                .boxed(),
1321            ),
1322            _ => None,
1323        }
1324    }
1325
1326    fn render_titlebar(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
1327        ConstrainedBox::new(
1328            Container::new(
1329                Stack::new()
1330                    .with_child(
1331                        Align::new(
1332                            Label::new("zed".into(), theme.workspace.titlebar.title.clone())
1333                                .boxed(),
1334                        )
1335                        .boxed(),
1336                    )
1337                    .with_child(
1338                        Align::new(
1339                            Flex::row()
1340                                .with_children(self.render_share_icon(theme, cx))
1341                                .with_children(self.render_collaborators(theme, cx))
1342                                .with_child(self.render_current_user(
1343                                    self.user_store.read(cx).current_user().as_ref(),
1344                                    self.project.read(cx).replica_id(),
1345                                    theme,
1346                                    cx,
1347                                ))
1348                                .with_children(self.render_connection_status(cx))
1349                                .boxed(),
1350                        )
1351                        .right()
1352                        .boxed(),
1353                    )
1354                    .boxed(),
1355            )
1356            .with_style(theme.workspace.titlebar.container)
1357            .boxed(),
1358        )
1359        .with_height(theme.workspace.titlebar.height)
1360        .named("titlebar")
1361    }
1362
1363    fn render_collaborators(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> Vec<ElementBox> {
1364        let mut collaborators = self
1365            .project
1366            .read(cx)
1367            .collaborators()
1368            .values()
1369            .cloned()
1370            .collect::<Vec<_>>();
1371        collaborators.sort_unstable_by_key(|collaborator| collaborator.replica_id);
1372        collaborators
1373            .into_iter()
1374            .filter_map(|collaborator| {
1375                Some(self.render_avatar(
1376                    collaborator.user.avatar.clone()?,
1377                    collaborator.replica_id,
1378                    Some(collaborator.peer_id),
1379                    theme,
1380                    cx,
1381                ))
1382            })
1383            .collect()
1384    }
1385
1386    fn render_current_user(
1387        &self,
1388        user: Option<&Arc<User>>,
1389        replica_id: ReplicaId,
1390        theme: &Theme,
1391        cx: &mut RenderContext<Self>,
1392    ) -> ElementBox {
1393        if let Some(avatar) = user.and_then(|user| user.avatar.clone()) {
1394            self.render_avatar(avatar, replica_id, None, theme, cx)
1395        } else {
1396            MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
1397                let style = if state.hovered {
1398                    &theme.workspace.titlebar.hovered_sign_in_prompt
1399                } else {
1400                    &theme.workspace.titlebar.sign_in_prompt
1401                };
1402                Label::new("Sign in".to_string(), style.text.clone())
1403                    .contained()
1404                    .with_style(style.container)
1405                    .boxed()
1406            })
1407            .on_click(|cx| cx.dispatch_action(Authenticate))
1408            .with_cursor_style(CursorStyle::PointingHand)
1409            .aligned()
1410            .boxed()
1411        }
1412    }
1413
1414    fn render_avatar(
1415        &self,
1416        avatar: Arc<ImageData>,
1417        replica_id: ReplicaId,
1418        peer_id: Option<PeerId>,
1419        theme: &Theme,
1420        cx: &mut RenderContext<Self>,
1421    ) -> ElementBox {
1422        let content = Stack::new()
1423            .with_child(
1424                Image::new(avatar)
1425                    .with_style(theme.workspace.titlebar.avatar)
1426                    .constrained()
1427                    .with_width(theme.workspace.titlebar.avatar_width)
1428                    .aligned()
1429                    .boxed(),
1430            )
1431            .with_child(
1432                AvatarRibbon::new(theme.editor.replica_selection_style(replica_id).cursor)
1433                    .constrained()
1434                    .with_width(theme.workspace.titlebar.avatar_ribbon.width)
1435                    .with_height(theme.workspace.titlebar.avatar_ribbon.height)
1436                    .aligned()
1437                    .bottom()
1438                    .boxed(),
1439            )
1440            .constrained()
1441            .with_width(theme.workspace.right_sidebar.width)
1442            .boxed();
1443
1444        if let Some(peer_id) = peer_id {
1445            MouseEventHandler::new::<ToggleFollow, _, _>(replica_id.into(), cx, move |_, _| content)
1446                .with_cursor_style(CursorStyle::PointingHand)
1447                .on_click(move |cx| cx.dispatch_action(ToggleFollow(peer_id)))
1448                .boxed()
1449        } else {
1450            content
1451        }
1452    }
1453
1454    fn render_share_icon(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
1455        if self.project().read(cx).is_local() && self.client.user_id().is_some() {
1456            let color = if self.project().read(cx).is_shared() {
1457                theme.workspace.titlebar.share_icon_active_color
1458            } else {
1459                theme.workspace.titlebar.share_icon_color
1460            };
1461            Some(
1462                MouseEventHandler::new::<ToggleShare, _, _>(0, cx, |_, _| {
1463                    Align::new(
1464                        Svg::new("icons/broadcast-24.svg")
1465                            .with_color(color)
1466                            .constrained()
1467                            .with_width(24.)
1468                            .boxed(),
1469                    )
1470                    .boxed()
1471                })
1472                .with_cursor_style(CursorStyle::PointingHand)
1473                .on_click(|cx| cx.dispatch_action(ToggleShare))
1474                .boxed(),
1475            )
1476        } else {
1477            None
1478        }
1479    }
1480
1481    fn render_disconnected_overlay(&self, cx: &AppContext) -> Option<ElementBox> {
1482        if self.project.read(cx).is_read_only() {
1483            let theme = &cx.global::<Settings>().theme;
1484            Some(
1485                EventHandler::new(
1486                    Label::new(
1487                        "Your connection to the remote project has been lost.".to_string(),
1488                        theme.workspace.disconnected_overlay.text.clone(),
1489                    )
1490                    .aligned()
1491                    .contained()
1492                    .with_style(theme.workspace.disconnected_overlay.container)
1493                    .boxed(),
1494                )
1495                .capture(|_, _, _| true)
1496                .boxed(),
1497            )
1498        } else {
1499            None
1500        }
1501    }
1502
1503    // RPC handlers
1504
1505    async fn handle_follow(
1506        this: ViewHandle<Self>,
1507        envelope: TypedEnvelope<proto::Follow>,
1508        _: Arc<Client>,
1509        mut cx: AsyncAppContext,
1510    ) -> Result<proto::FollowResponse> {
1511        this.update(&mut cx, |this, cx| {
1512            this.leader_state
1513                .followers
1514                .insert(envelope.original_sender_id()?);
1515
1516            let active_view_id = this
1517                .active_item(cx)
1518                .and_then(|i| i.to_followable_item_handle(cx))
1519                .map(|i| i.id() as u64);
1520            Ok(proto::FollowResponse {
1521                active_view_id,
1522                views: this
1523                    .items(cx)
1524                    .filter_map(|item| {
1525                        let id = item.id() as u64;
1526                        let item = item.to_followable_item_handle(cx)?;
1527                        let variant = item.to_state_message(cx)?;
1528                        Some(proto::View {
1529                            id,
1530                            variant: Some(variant),
1531                        })
1532                    })
1533                    .collect(),
1534            })
1535        })
1536    }
1537
1538    async fn handle_unfollow(
1539        this: ViewHandle<Self>,
1540        envelope: TypedEnvelope<proto::Unfollow>,
1541        _: Arc<Client>,
1542        mut cx: AsyncAppContext,
1543    ) -> Result<()> {
1544        this.update(&mut cx, |this, _| {
1545            this.leader_state
1546                .followers
1547                .remove(&envelope.original_sender_id()?);
1548            Ok(())
1549        })
1550    }
1551
1552    async fn handle_update_followers(
1553        this: ViewHandle<Self>,
1554        envelope: TypedEnvelope<proto::UpdateFollowers>,
1555        _: Arc<Client>,
1556        mut cx: AsyncAppContext,
1557    ) -> Result<()> {
1558        let leader_id = envelope.original_sender_id()?;
1559        match envelope
1560            .payload
1561            .variant
1562            .ok_or_else(|| anyhow!("invalid update"))?
1563        {
1564            proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
1565                this.update(&mut cx, |this, cx| {
1566                    this.update_leader_state(leader_id, cx, |state, _| {
1567                        state.active_view_id = update_active_view.id;
1568                    });
1569                    Ok::<_, anyhow::Error>(())
1570                })
1571            }
1572            proto::update_followers::Variant::UpdateView(update_view) => {
1573                this.update(&mut cx, |this, cx| {
1574                    let variant = update_view
1575                        .variant
1576                        .ok_or_else(|| anyhow!("missing update view variant"))?;
1577                    this.update_leader_state(leader_id, cx, |state, cx| {
1578                        let variant = variant.clone();
1579                        match state
1580                            .items_by_leader_view_id
1581                            .entry(update_view.id)
1582                            .or_insert(FollowerItem::Loading(Vec::new()))
1583                        {
1584                            FollowerItem::Loaded(item) => {
1585                                item.apply_update_message(variant, cx).log_err();
1586                            }
1587                            FollowerItem::Loading(updates) => updates.push(variant),
1588                        }
1589                    });
1590                    Ok(())
1591                })
1592            }
1593            proto::update_followers::Variant::CreateView(view) => {
1594                let panes = this.read_with(&cx, |this, _| {
1595                    this.follower_states_by_leader
1596                        .get(&leader_id)
1597                        .into_iter()
1598                        .flat_map(|states_by_pane| states_by_pane.keys())
1599                        .cloned()
1600                        .collect()
1601                });
1602                Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], &mut cx)
1603                    .await?;
1604                Ok(())
1605            }
1606        }
1607        .log_err();
1608
1609        Ok(())
1610    }
1611
1612    async fn add_views_from_leader(
1613        this: ViewHandle<Self>,
1614        leader_id: PeerId,
1615        panes: Vec<ViewHandle<Pane>>,
1616        views: Vec<proto::View>,
1617        cx: &mut AsyncAppContext,
1618    ) -> Result<()> {
1619        let project = this.read_with(cx, |this, _| this.project.clone());
1620
1621        let item_builders = cx.update(|cx| {
1622            cx.default_global::<FollowableItemBuilders>()
1623                .values()
1624                .map(|b| b.0)
1625                .collect::<Vec<_>>()
1626                .clone()
1627        });
1628
1629        let mut item_tasks_by_pane = HashMap::default();
1630        for pane in panes {
1631            let mut item_tasks = Vec::new();
1632            let mut leader_view_ids = Vec::new();
1633            for view in &views {
1634                let mut variant = view.variant.clone();
1635                if variant.is_none() {
1636                    Err(anyhow!("missing variant"))?;
1637                }
1638                for build_item in &item_builders {
1639                    let task =
1640                        cx.update(|cx| build_item(pane.clone(), project.clone(), &mut variant, cx));
1641                    if let Some(task) = task {
1642                        item_tasks.push(task);
1643                        leader_view_ids.push(view.id);
1644                        break;
1645                    } else {
1646                        assert!(variant.is_some());
1647                    }
1648                }
1649            }
1650
1651            item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids));
1652        }
1653
1654        for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane {
1655            let items = futures::future::try_join_all(item_tasks).await?;
1656            this.update(cx, |this, cx| {
1657                let state = this
1658                    .follower_states_by_leader
1659                    .get_mut(&leader_id)?
1660                    .get_mut(&pane)?;
1661
1662                for (id, item) in leader_view_ids.into_iter().zip(items) {
1663                    item.set_following(true, cx);
1664                    match state.items_by_leader_view_id.entry(id) {
1665                        hash_map::Entry::Occupied(e) => {
1666                            let e = e.into_mut();
1667                            if let FollowerItem::Loading(updates) = e {
1668                                for update in updates.drain(..) {
1669                                    item.apply_update_message(update, cx)
1670                                        .context("failed to apply view update")
1671                                        .log_err();
1672                                }
1673                            }
1674                            *e = FollowerItem::Loaded(item);
1675                        }
1676                        hash_map::Entry::Vacant(e) => {
1677                            e.insert(FollowerItem::Loaded(item));
1678                        }
1679                    }
1680                }
1681
1682                Some(())
1683            });
1684        }
1685        this.update(cx, |this, cx| this.leader_updated(leader_id, cx));
1686
1687        Ok(())
1688    }
1689
1690    fn update_followers(
1691        &self,
1692        update: proto::update_followers::Variant,
1693        cx: &AppContext,
1694    ) -> Option<()> {
1695        let project_id = self.project.read(cx).remote_id()?;
1696        if !self.leader_state.followers.is_empty() {
1697            self.client
1698                .send(proto::UpdateFollowers {
1699                    project_id,
1700                    follower_ids: self.leader_state.followers.iter().map(|f| f.0).collect(),
1701                    variant: Some(update),
1702                })
1703                .log_err();
1704        }
1705        None
1706    }
1707
1708    fn update_leader_state(
1709        &mut self,
1710        leader_id: PeerId,
1711        cx: &mut ViewContext<Self>,
1712        mut update_fn: impl FnMut(&mut FollowerState, &mut ViewContext<Self>),
1713    ) {
1714        for (_, state) in self
1715            .follower_states_by_leader
1716            .get_mut(&leader_id)
1717            .into_iter()
1718            .flatten()
1719        {
1720            update_fn(state, cx);
1721        }
1722        self.leader_updated(leader_id, cx);
1723    }
1724
1725    fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
1726        let mut items_to_add = Vec::new();
1727        for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
1728            if let Some(active_item) = state
1729                .active_view_id
1730                .and_then(|id| state.items_by_leader_view_id.get(&id))
1731            {
1732                if let FollowerItem::Loaded(item) = active_item {
1733                    items_to_add.push((pane.clone(), item.boxed_clone()));
1734                }
1735            }
1736        }
1737
1738        for (pane, item) in items_to_add {
1739            Pane::add_item(self, pane.clone(), item.boxed_clone(), cx);
1740            cx.notify();
1741        }
1742        None
1743    }
1744}
1745
1746impl Entity for Workspace {
1747    type Event = ();
1748}
1749
1750impl View for Workspace {
1751    fn ui_name() -> &'static str {
1752        "Workspace"
1753    }
1754
1755    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
1756        let theme = cx.global::<Settings>().theme.clone();
1757        Stack::new()
1758            .with_child(
1759                Flex::column()
1760                    .with_child(self.render_titlebar(&theme, cx))
1761                    .with_child(
1762                        Stack::new()
1763                            .with_child({
1764                                let mut content = Flex::row();
1765                                content.add_child(self.left_sidebar.render(&theme, cx));
1766                                if let Some(element) =
1767                                    self.left_sidebar.render_active_item(&theme, cx)
1768                                {
1769                                    content.add_child(Flexible::new(0.8, false, element).boxed());
1770                                }
1771                                content.add_child(
1772                                    Flex::column()
1773                                        .with_child(
1774                                            Flexible::new(1., true, self.center.render(&theme))
1775                                                .boxed(),
1776                                        )
1777                                        .with_child(ChildView::new(&self.status_bar).boxed())
1778                                        .flexible(1., true)
1779                                        .boxed(),
1780                                );
1781                                if let Some(element) =
1782                                    self.right_sidebar.render_active_item(&theme, cx)
1783                                {
1784                                    content.add_child(Flexible::new(0.8, false, element).boxed());
1785                                }
1786                                content.add_child(self.right_sidebar.render(&theme, cx));
1787                                content.boxed()
1788                            })
1789                            .with_children(self.modal.as_ref().map(|m| ChildView::new(m).boxed()))
1790                            .flexible(1.0, true)
1791                            .boxed(),
1792                    )
1793                    .contained()
1794                    .with_background_color(theme.workspace.background)
1795                    .boxed(),
1796            )
1797            .with_children(self.render_disconnected_overlay(cx))
1798            .named("workspace")
1799    }
1800
1801    fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
1802        cx.focus(&self.active_pane);
1803    }
1804}
1805
1806pub trait WorkspaceHandle {
1807    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
1808}
1809
1810impl WorkspaceHandle for ViewHandle<Workspace> {
1811    fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
1812        self.read(cx)
1813            .worktrees(cx)
1814            .flat_map(|worktree| {
1815                let worktree_id = worktree.read(cx).id();
1816                worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
1817                    worktree_id,
1818                    path: f.path.clone(),
1819                })
1820            })
1821            .collect::<Vec<_>>()
1822    }
1823}
1824
1825pub struct AvatarRibbon {
1826    color: Color,
1827}
1828
1829impl AvatarRibbon {
1830    pub fn new(color: Color) -> AvatarRibbon {
1831        AvatarRibbon { color }
1832    }
1833}
1834
1835impl Element for AvatarRibbon {
1836    type LayoutState = ();
1837
1838    type PaintState = ();
1839
1840    fn layout(
1841        &mut self,
1842        constraint: gpui::SizeConstraint,
1843        _: &mut gpui::LayoutContext,
1844    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
1845        (constraint.max, ())
1846    }
1847
1848    fn paint(
1849        &mut self,
1850        bounds: gpui::geometry::rect::RectF,
1851        _: gpui::geometry::rect::RectF,
1852        _: &mut Self::LayoutState,
1853        cx: &mut gpui::PaintContext,
1854    ) -> Self::PaintState {
1855        let mut path = PathBuilder::new();
1856        path.reset(bounds.lower_left());
1857        path.curve_to(
1858            bounds.origin() + vec2f(bounds.height(), 0.),
1859            bounds.origin(),
1860        );
1861        path.line_to(bounds.upper_right() - vec2f(bounds.height(), 0.));
1862        path.curve_to(bounds.lower_right(), bounds.upper_right());
1863        path.line_to(bounds.lower_left());
1864        cx.scene.push_path(path.build(self.color, None));
1865    }
1866
1867    fn dispatch_event(
1868        &mut self,
1869        _: &gpui::Event,
1870        _: gpui::geometry::rect::RectF,
1871        _: &mut Self::LayoutState,
1872        _: &mut Self::PaintState,
1873        _: &mut gpui::EventContext,
1874    ) -> bool {
1875        false
1876    }
1877
1878    fn debug(
1879        &self,
1880        bounds: gpui::geometry::rect::RectF,
1881        _: &Self::LayoutState,
1882        _: &Self::PaintState,
1883        _: &gpui::DebugContext,
1884    ) -> gpui::json::Value {
1885        json::json!({
1886            "type": "AvatarRibbon",
1887            "bounds": bounds.to_json(),
1888            "color": self.color.to_json(),
1889        })
1890    }
1891}
1892
1893impl std::fmt::Debug for OpenParams {
1894    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1895        f.debug_struct("OpenParams")
1896            .field("paths", &self.paths)
1897            .finish()
1898    }
1899}
1900
1901fn open(action: &Open, cx: &mut MutableAppContext) {
1902    let app_state = action.0.clone();
1903    let mut paths = cx.prompt_for_paths(PathPromptOptions {
1904        files: true,
1905        directories: true,
1906        multiple: true,
1907    });
1908    cx.spawn(|mut cx| async move {
1909        if let Some(paths) = paths.recv().await.flatten() {
1910            cx.update(|cx| cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state })));
1911        }
1912    })
1913    .detach();
1914}
1915
1916pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
1917
1918pub fn open_paths(
1919    abs_paths: &[PathBuf],
1920    app_state: &Arc<AppState>,
1921    cx: &mut MutableAppContext,
1922) -> Task<ViewHandle<Workspace>> {
1923    log::info!("open paths {:?}", abs_paths);
1924
1925    // Open paths in existing workspace if possible
1926    let mut existing = None;
1927    for window_id in cx.window_ids().collect::<Vec<_>>() {
1928        if let Some(workspace_handle) = cx.root_view::<Workspace>(window_id) {
1929            if workspace_handle.update(cx, |workspace, cx| {
1930                if workspace.contains_paths(abs_paths, cx.as_ref()) {
1931                    cx.activate_window(window_id);
1932                    existing = Some(workspace_handle.clone());
1933                    true
1934                } else {
1935                    false
1936                }
1937            }) {
1938                break;
1939            }
1940        }
1941    }
1942
1943    let workspace = existing.unwrap_or_else(|| {
1944        cx.add_window((app_state.build_window_options)(), |cx| {
1945            let project = Project::local(
1946                app_state.client.clone(),
1947                app_state.user_store.clone(),
1948                app_state.languages.clone(),
1949                app_state.fs.clone(),
1950                cx,
1951            );
1952            (app_state.build_workspace)(project, &app_state, cx)
1953        })
1954        .1
1955    });
1956
1957    let task = workspace.update(cx, |workspace, cx| workspace.open_paths(abs_paths, cx));
1958    cx.spawn(|_| async move {
1959        task.await;
1960        workspace
1961    })
1962}
1963
1964pub fn join_project(
1965    project_id: u64,
1966    app_state: &Arc<AppState>,
1967    cx: &mut MutableAppContext,
1968) -> Task<Result<ViewHandle<Workspace>>> {
1969    for window_id in cx.window_ids().collect::<Vec<_>>() {
1970        if let Some(workspace) = cx.root_view::<Workspace>(window_id) {
1971            if workspace.read(cx).project().read(cx).remote_id() == Some(project_id) {
1972                return Task::ready(Ok(workspace));
1973            }
1974        }
1975    }
1976
1977    let app_state = app_state.clone();
1978    cx.spawn(|mut cx| async move {
1979        let project = Project::remote(
1980            project_id,
1981            app_state.client.clone(),
1982            app_state.user_store.clone(),
1983            app_state.languages.clone(),
1984            app_state.fs.clone(),
1985            &mut cx,
1986        )
1987        .await?;
1988        Ok(cx.update(|cx| {
1989            cx.add_window((app_state.build_window_options)(), |cx| {
1990                (app_state.build_workspace)(project, &app_state, cx)
1991            })
1992            .1
1993        }))
1994    })
1995}
1996
1997fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
1998    let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
1999        let project = Project::local(
2000            app_state.client.clone(),
2001            app_state.user_store.clone(),
2002            app_state.languages.clone(),
2003            app_state.fs.clone(),
2004            cx,
2005        );
2006        (app_state.build_workspace)(project, &app_state, cx)
2007    });
2008    cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone()));
2009}