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