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