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