workspace.rs

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