workspace.rs

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