item.rs

   1// use crate::{
   2//     pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
   3//     ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
   4// };
   5// use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
   6// use anyhow::Result;
   7// use client2::{
   8//     proto::{self, PeerId},
   9//     Client,
  10// };
  11// use gpui2::geometry::vector::Vector2F;
  12// use gpui2::AnyWindowHandle;
  13// use gpui2::{
  14//     fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
  15//     ViewContext, ViewHandle, WeakViewHandle, WindowContext,
  16// };
  17// use project2::{Project, ProjectEntryId, ProjectPath};
  18// use schemars::JsonSchema;
  19// use serde_derive::{Deserialize, Serialize};
  20// use settings2::Setting;
  21// use smallvec::SmallVec;
  22// use std::{
  23//     any::{Any, TypeId},
  24//     borrow::Cow,
  25//     cell::RefCell,
  26//     fmt,
  27//     ops::Range,
  28//     path::PathBuf,
  29//     rc::Rc,
  30//     sync::{
  31//         atomic::{AtomicBool, Ordering},
  32//         Arc,
  33//     },
  34//     time::Duration,
  35// };
  36// use theme2::Theme;
  37
  38// #[derive(Deserialize)]
  39// pub struct ItemSettings {
  40//     pub git_status: bool,
  41//     pub close_position: ClosePosition,
  42// }
  43
  44// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
  45// #[serde(rename_all = "lowercase")]
  46// pub enum ClosePosition {
  47//     Left,
  48//     #[default]
  49//     Right,
  50// }
  51
  52// impl ClosePosition {
  53//     pub fn right(&self) -> bool {
  54//         match self {
  55//             ClosePosition::Left => false,
  56//             ClosePosition::Right => true,
  57//         }
  58//     }
  59// }
  60
  61// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
  62// pub struct ItemSettingsContent {
  63//     git_status: Option<bool>,
  64//     close_position: Option<ClosePosition>,
  65// }
  66
  67// impl Setting for ItemSettings {
  68//     const KEY: Option<&'static str> = Some("tabs");
  69
  70//     type FileContent = ItemSettingsContent;
  71
  72//     fn load(
  73//         default_value: &Self::FileContent,
  74//         user_values: &[&Self::FileContent],
  75//         _: &gpui2::AppContext,
  76//     ) -> anyhow::Result<Self> {
  77//         Self::load_via_json_merge(default_value, user_values)
  78//     }
  79// }
  80
  81// #[derive(Eq, PartialEq, Hash, Debug)]
  82// pub enum ItemEvent {
  83//     CloseItem,
  84//     UpdateTab,
  85//     UpdateBreadcrumbs,
  86//     Edit,
  87// }
  88
  89// // TODO: Combine this with existing HighlightedText struct?
  90// pub struct BreadcrumbText {
  91//     pub text: String,
  92//     pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
  93// }
  94
  95// pub trait Item: View {
  96//     fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
  97//     fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
  98//     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
  99//         false
 100//     }
 101//     fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
 102//         None
 103//     }
 104//     fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
 105//         None
 106//     }
 107//     fn tab_content<V: 'static>(
 108//         &self,
 109//         detail: Option<usize>,
 110//         style: &theme2::Tab,
 111//         cx: &AppContext,
 112//     ) -> AnyElement<V>;
 113//     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) {
 114//     } // (model id, Item)
 115//     fn is_singleton(&self, _cx: &AppContext) -> bool {
 116//         false
 117//     }
 118//     fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
 119//     fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
 120//     where
 121//         Self: Sized,
 122//     {
 123//         None
 124//     }
 125//     fn is_dirty(&self, _: &AppContext) -> bool {
 126//         false
 127//     }
 128//     fn has_conflict(&self, _: &AppContext) -> bool {
 129//         false
 130//     }
 131//     fn can_save(&self, _cx: &AppContext) -> bool {
 132//         false
 133//     }
 134//     fn save(
 135//         &mut self,
 136//         _project: ModelHandle<Project>,
 137//         _cx: &mut ViewContext<Self>,
 138//     ) -> Task<Result<()>> {
 139//         unimplemented!("save() must be implemented if can_save() returns true")
 140//     }
 141//     fn save_as(
 142//         &mut self,
 143//         _project: ModelHandle<Project>,
 144//         _abs_path: PathBuf,
 145//         _cx: &mut ViewContext<Self>,
 146//     ) -> Task<Result<()>> {
 147//         unimplemented!("save_as() must be implemented if can_save() returns true")
 148//     }
 149//     fn reload(
 150//         &mut self,
 151//         _project: ModelHandle<Project>,
 152//         _cx: &mut ViewContext<Self>,
 153//     ) -> Task<Result<()>> {
 154//         unimplemented!("reload() must be implemented if can_save() returns true")
 155//     }
 156//     fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
 157//         SmallVec::new()
 158//     }
 159//     fn should_close_item_on_event(_: &Self::Event) -> bool {
 160//         false
 161//     }
 162//     fn should_update_tab_on_event(_: &Self::Event) -> bool {
 163//         false
 164//     }
 165
 166//     fn act_as_type<'a>(
 167//         &'a self,
 168//         type_id: TypeId,
 169//         self_handle: &'a ViewHandle<Self>,
 170//         _: &'a AppContext,
 171//     ) -> Option<&AnyViewHandle> {
 172//         if TypeId::of::<Self>() == type_id {
 173//             Some(self_handle)
 174//         } else {
 175//             None
 176//         }
 177//     }
 178
 179//     fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
 180//         None
 181//     }
 182
 183//     fn breadcrumb_location(&self) -> ToolbarItemLocation {
 184//         ToolbarItemLocation::Hidden
 185//     }
 186
 187//     fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
 188//         None
 189//     }
 190
 191//     fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
 192
 193//     fn serialized_item_kind() -> Option<&'static str> {
 194//         None
 195//     }
 196
 197//     fn deserialize(
 198//         _project: ModelHandle<Project>,
 199//         _workspace: WeakViewHandle<Workspace>,
 200//         _workspace_id: WorkspaceId,
 201//         _item_id: ItemId,
 202//         _cx: &mut ViewContext<Pane>,
 203//     ) -> Task<Result<ViewHandle<Self>>> {
 204//         unimplemented!(
 205//             "deserialize() must be implemented if serialized_item_kind() returns Some(_)"
 206//         )
 207//     }
 208//     fn show_toolbar(&self) -> bool {
 209//         true
 210//     }
 211//     fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
 212//         None
 213//     }
 214// }
 215
 216use core::fmt;
 217
 218pub trait ItemHandle: 'static + fmt::Debug + Send + Sync {
 219    //     fn subscribe_to_item_events(
 220    //         &self,
 221    //         cx: &mut WindowContext,
 222    //         handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
 223    //     ) -> gpui2::Subscription;
 224    //     fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>>;
 225    //     fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
 226    //     fn tab_content(
 227    //         &self,
 228    //         detail: Option<usize>,
 229    //         style: &theme2::Tab,
 230    //         cx: &AppContext,
 231    //     ) -> AnyElement<Pane>;
 232    //     fn dragged_tab_content(
 233    //         &self,
 234    //         detail: Option<usize>,
 235    //         style: &theme2::Tab,
 236    //         cx: &AppContext,
 237    //     ) -> AnyElement<Workspace>;
 238    //     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 239    //     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
 240    //     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]>;
 241    //     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item));
 242    //     fn is_singleton(&self, cx: &AppContext) -> bool;
 243    //     fn boxed_clone(&self) -> Box<dyn ItemHandle>;
 244    //     fn clone_on_split(
 245    //         &self,
 246    //         workspace_id: WorkspaceId,
 247    //         cx: &mut WindowContext,
 248    //     ) -> Option<Box<dyn ItemHandle>>;
 249    //     fn added_to_pane(
 250    //         &self,
 251    //         workspace: &mut Workspace,
 252    //         pane: ViewHandle<Pane>,
 253    //         cx: &mut ViewContext<Workspace>,
 254    //     );
 255    //     fn deactivated(&self, cx: &mut WindowContext);
 256    //     fn workspace_deactivated(&self, cx: &mut WindowContext);
 257    //     fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
 258    //     fn id(&self) -> usize;
 259    //     fn window(&self) -> AnyWindowHandle;
 260    //     fn as_any(&self) -> &AnyViewHandle;
 261    //     fn is_dirty(&self, cx: &AppContext) -> bool;
 262    //     fn has_conflict(&self, cx: &AppContext) -> bool;
 263    //     fn can_save(&self, cx: &AppContext) -> bool;
 264    //     fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
 265    //     fn save_as(
 266    //         &self,
 267    //         project: ModelHandle<Project>,
 268    //         abs_path: PathBuf,
 269    //         cx: &mut WindowContext,
 270    //     ) -> Task<Result<()>>;
 271    //     fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
 272    //     fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>;
 273    //     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
 274    //     fn on_release(
 275    //         &self,
 276    //         cx: &mut AppContext,
 277    //         callback: Box<dyn FnOnce(&mut AppContext)>,
 278    //     ) -> gpui2::Subscription;
 279    //     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
 280    //     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
 281    //     fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
 282    //     fn serialized_item_kind(&self) -> Option<&'static str>;
 283    //     fn show_toolbar(&self, cx: &AppContext) -> bool;
 284    //     fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F>;
 285}
 286
 287// pub trait WeakItemHandle {
 288//     fn id(&self) -> usize;
 289//     fn window(&self) -> AnyWindowHandle;
 290//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
 291// }
 292
 293// impl dyn ItemHandle {
 294//     pub fn downcast<T: View>(&self) -> Option<ViewHandle<T>> {
 295//         self.as_any().clone().downcast()
 296//     }
 297
 298//     pub fn act_as<T: View>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
 299//         self.act_as_type(TypeId::of::<T>(), cx)
 300//             .and_then(|t| t.clone().downcast())
 301//     }
 302// }
 303
 304// impl<T: Item> ItemHandle for ViewHandle<T> {
 305//     fn subscribe_to_item_events(
 306//         &self,
 307//         cx: &mut WindowContext,
 308//         handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
 309//     ) -> gpui2::Subscription {
 310//         cx.subscribe(self, move |_, event, cx| {
 311//             for item_event in T::to_item_events(event) {
 312//                 handler(item_event, cx)
 313//             }
 314//         })
 315//     }
 316
 317//     fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>> {
 318//         self.read(cx).tab_tooltip_text(cx)
 319//     }
 320
 321//     fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>> {
 322//         self.read(cx).tab_description(detail, cx)
 323//     }
 324
 325//     fn tab_content(
 326//         &self,
 327//         detail: Option<usize>,
 328//         style: &theme2::Tab,
 329//         cx: &AppContext,
 330//     ) -> AnyElement<Pane> {
 331//         self.read(cx).tab_content(detail, style, cx)
 332//     }
 333
 334//     fn dragged_tab_content(
 335//         &self,
 336//         detail: Option<usize>,
 337//         style: &theme2::Tab,
 338//         cx: &AppContext,
 339//     ) -> AnyElement<Workspace> {
 340//         self.read(cx).tab_content(detail, style, cx)
 341//     }
 342
 343//     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
 344//         let this = self.read(cx);
 345//         let mut result = None;
 346//         if this.is_singleton(cx) {
 347//             this.for_each_project_item(cx, &mut |_, item| {
 348//                 result = item.project_path(cx);
 349//             });
 350//         }
 351//         result
 352//     }
 353
 354//     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> {
 355//         let mut result = SmallVec::new();
 356//         self.read(cx).for_each_project_item(cx, &mut |_, item| {
 357//             if let Some(id) = item.entry_id(cx) {
 358//                 result.push(id);
 359//             }
 360//         });
 361//         result
 362//     }
 363
 364//     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]> {
 365//         let mut result = SmallVec::new();
 366//         self.read(cx).for_each_project_item(cx, &mut |id, _| {
 367//             result.push(id);
 368//         });
 369//         result
 370//     }
 371
 372//     fn for_each_project_item(
 373//         &self,
 374//         cx: &AppContext,
 375//         f: &mut dyn FnMut(usize, &dyn project2::Item),
 376//     ) {
 377//         self.read(cx).for_each_project_item(cx, f)
 378//     }
 379
 380//     fn is_singleton(&self, cx: &AppContext) -> bool {
 381//         self.read(cx).is_singleton(cx)
 382//     }
 383
 384//     fn boxed_clone(&self) -> Box<dyn ItemHandle> {
 385//         Box::new(self.clone())
 386//     }
 387
 388//     fn clone_on_split(
 389//         &self,
 390//         workspace_id: WorkspaceId,
 391//         cx: &mut WindowContext,
 392//     ) -> Option<Box<dyn ItemHandle>> {
 393//         self.update(cx, |item, cx| {
 394//             cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
 395//         })
 396//         .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
 397//     }
 398
 399//     fn added_to_pane(
 400//         &self,
 401//         workspace: &mut Workspace,
 402//         pane: ViewHandle<Pane>,
 403//         cx: &mut ViewContext<Workspace>,
 404//     ) {
 405//         let history = pane.read(cx).nav_history_for_item(self);
 406//         self.update(cx, |this, cx| {
 407//             this.set_nav_history(history, cx);
 408//             this.added_to_workspace(workspace, cx);
 409//         });
 410
 411//         if let Some(followed_item) = self.to_followable_item_handle(cx) {
 412//             if let Some(message) = followed_item.to_state_proto(cx) {
 413//                 workspace.update_followers(
 414//                     followed_item.is_project_item(cx),
 415//                     proto::update_followers::Variant::CreateView(proto::View {
 416//                         id: followed_item
 417//                             .remote_id(&workspace.app_state.client, cx)
 418//                             .map(|id| id.to_proto()),
 419//                         variant: Some(message),
 420//                         leader_id: workspace.leader_for_pane(&pane),
 421//                     }),
 422//                     cx,
 423//                 );
 424//             }
 425//         }
 426
 427//         if workspace
 428//             .panes_by_item
 429//             .insert(self.id(), pane.downgrade())
 430//             .is_none()
 431//         {
 432//             let mut pending_autosave = DelayedDebouncedEditAction::new();
 433//             let pending_update = Rc::new(RefCell::new(None));
 434//             let pending_update_scheduled = Rc::new(AtomicBool::new(false));
 435
 436//             let mut event_subscription =
 437//                 Some(cx.subscribe(self, move |workspace, item, event, cx| {
 438//                     let pane = if let Some(pane) = workspace
 439//                         .panes_by_item
 440//                         .get(&item.id())
 441//                         .and_then(|pane| pane.upgrade(cx))
 442//                     {
 443//                         pane
 444//                     } else {
 445//                         log::error!("unexpected item event after pane was dropped");
 446//                         return;
 447//                     };
 448
 449//                     if let Some(item) = item.to_followable_item_handle(cx) {
 450//                         let is_project_item = item.is_project_item(cx);
 451//                         let leader_id = workspace.leader_for_pane(&pane);
 452
 453//                         if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
 454//                             workspace.unfollow(&pane, cx);
 455//                         }
 456
 457//                         if item.add_event_to_update_proto(
 458//                             event,
 459//                             &mut *pending_update.borrow_mut(),
 460//                             cx,
 461//                         ) && !pending_update_scheduled.load(Ordering::SeqCst)
 462//                         {
 463//                             pending_update_scheduled.store(true, Ordering::SeqCst);
 464//                             cx.after_window_update({
 465//                                 let pending_update = pending_update.clone();
 466//                                 let pending_update_scheduled = pending_update_scheduled.clone();
 467//                                 move |this, cx| {
 468//                                     pending_update_scheduled.store(false, Ordering::SeqCst);
 469//                                     this.update_followers(
 470//                                         is_project_item,
 471//                                         proto::update_followers::Variant::UpdateView(
 472//                                             proto::UpdateView {
 473//                                                 id: item
 474//                                                     .remote_id(&this.app_state.client, cx)
 475//                                                     .map(|id| id.to_proto()),
 476//                                                 variant: pending_update.borrow_mut().take(),
 477//                                                 leader_id,
 478//                                             },
 479//                                         ),
 480//                                         cx,
 481//                                     );
 482//                                 }
 483//                             });
 484//                         }
 485//                     }
 486
 487//                     for item_event in T::to_item_events(event).into_iter() {
 488//                         match item_event {
 489//                             ItemEvent::CloseItem => {
 490//                                 pane.update(cx, |pane, cx| {
 491//                                     pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
 492//                                 })
 493//                                 .detach_and_log_err(cx);
 494//                                 return;
 495//                             }
 496
 497//                             ItemEvent::UpdateTab => {
 498//                                 pane.update(cx, |_, cx| {
 499//                                     cx.emit(pane::Event::ChangeItemTitle);
 500//                                     cx.notify();
 501//                                 });
 502//                             }
 503
 504//                             ItemEvent::Edit => {
 505//                                 let autosave = settings2::get::<WorkspaceSettings>(cx).autosave;
 506//                                 if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
 507//                                     let delay = Duration::from_millis(milliseconds);
 508//                                     let item = item.clone();
 509//                                     pending_autosave.fire_new(delay, cx, move |workspace, cx| {
 510//                                         Pane::autosave_item(&item, workspace.project().clone(), cx)
 511//                                     });
 512//                                 }
 513//                             }
 514
 515//                             _ => {}
 516//                         }
 517//                     }
 518//                 }));
 519
 520//             cx.observe_focus(self, move |workspace, item, focused, cx| {
 521//                 if !focused
 522//                     && settings2::get::<WorkspaceSettings>(cx).autosave
 523//                         == AutosaveSetting::OnFocusChange
 524//                 {
 525//                     Pane::autosave_item(&item, workspace.project.clone(), cx)
 526//                         .detach_and_log_err(cx);
 527//                 }
 528//             })
 529//             .detach();
 530
 531//             let item_id = self.id();
 532//             cx.observe_release(self, move |workspace, _, _| {
 533//                 workspace.panes_by_item.remove(&item_id);
 534//                 event_subscription.take();
 535//             })
 536//             .detach();
 537//         }
 538
 539//         cx.defer(|workspace, cx| {
 540//             workspace.serialize_workspace(cx);
 541//         });
 542//     }
 543
 544//     fn deactivated(&self, cx: &mut WindowContext) {
 545//         self.update(cx, |this, cx| this.deactivated(cx));
 546//     }
 547
 548//     fn workspace_deactivated(&self, cx: &mut WindowContext) {
 549//         self.update(cx, |this, cx| this.workspace_deactivated(cx));
 550//     }
 551
 552//     fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
 553//         self.update(cx, |this, cx| this.navigate(data, cx))
 554//     }
 555
 556//     fn id(&self) -> usize {
 557//         self.id()
 558//     }
 559
 560//     fn window(&self) -> AnyWindowHandle {
 561//         AnyViewHandle::window(self)
 562//     }
 563
 564//     fn as_any(&self) -> &AnyViewHandle {
 565//         self
 566//     }
 567
 568//     fn is_dirty(&self, cx: &AppContext) -> bool {
 569//         self.read(cx).is_dirty(cx)
 570//     }
 571
 572//     fn has_conflict(&self, cx: &AppContext) -> bool {
 573//         self.read(cx).has_conflict(cx)
 574//     }
 575
 576//     fn can_save(&self, cx: &AppContext) -> bool {
 577//         self.read(cx).can_save(cx)
 578//     }
 579
 580//     fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
 581//         self.update(cx, |item, cx| item.save(project, cx))
 582//     }
 583
 584//     fn save_as(
 585//         &self,
 586//         project: ModelHandle<Project>,
 587//         abs_path: PathBuf,
 588//         cx: &mut WindowContext,
 589//     ) -> Task<anyhow::Result<()>> {
 590//         self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
 591//     }
 592
 593//     fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
 594//         self.update(cx, |item, cx| item.reload(project, cx))
 595//     }
 596
 597//     fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> {
 598//         self.read(cx).act_as_type(type_id, self, cx)
 599//     }
 600
 601//     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
 602//         if cx.has_global::<FollowableItemBuilders>() {
 603//             let builders = cx.global::<FollowableItemBuilders>();
 604//             let item = self.as_any();
 605//             Some(builders.get(&item.view_type())?.1(item))
 606//         } else {
 607//             None
 608//         }
 609//     }
 610
 611//     fn on_release(
 612//         &self,
 613//         cx: &mut AppContext,
 614//         callback: Box<dyn FnOnce(&mut AppContext)>,
 615//     ) -> gpui2::Subscription {
 616//         cx.observe_release(self, move |_, cx| callback(cx))
 617//     }
 618
 619//     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
 620//         self.read(cx).as_searchable(self)
 621//     }
 622
 623//     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
 624//         self.read(cx).breadcrumb_location()
 625//     }
 626
 627//     fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
 628//         self.read(cx).breadcrumbs(theme, cx)
 629//     }
 630
 631//     fn serialized_item_kind(&self) -> Option<&'static str> {
 632//         T::serialized_item_kind()
 633//     }
 634
 635//     fn show_toolbar(&self, cx: &AppContext) -> bool {
 636//         self.read(cx).show_toolbar()
 637//     }
 638
 639//     fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
 640//         self.read(cx).pixel_position_of_cursor(cx)
 641//     }
 642// }
 643
 644// impl From<Box<dyn ItemHandle>> for AnyViewHandle {
 645//     fn from(val: Box<dyn ItemHandle>) -> Self {
 646//         val.as_any().clone()
 647//     }
 648// }
 649
 650// impl From<&Box<dyn ItemHandle>> for AnyViewHandle {
 651//     fn from(val: &Box<dyn ItemHandle>) -> Self {
 652//         val.as_any().clone()
 653//     }
 654// }
 655
 656// impl Clone for Box<dyn ItemHandle> {
 657//     fn clone(&self) -> Box<dyn ItemHandle> {
 658//         self.boxed_clone()
 659//     }
 660// }
 661
 662// impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
 663//     fn id(&self) -> usize {
 664//         self.id()
 665//     }
 666
 667//     fn window(&self) -> AnyWindowHandle {
 668//         self.window()
 669//     }
 670
 671//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
 672//         self.upgrade(cx).map(|v| Box::new(v) as Box<dyn ItemHandle>)
 673//     }
 674// }
 675
 676// pub trait ProjectItem: Item {
 677//     type Item: project2::Item + gpui2::Entity;
 678
 679//     fn for_project_item(
 680//         project: ModelHandle<Project>,
 681//         item: ModelHandle<Self::Item>,
 682//         cx: &mut ViewContext<Self>,
 683//     ) -> Self;
 684// }
 685
 686// pub trait FollowableItem: Item {
 687//     fn remote_id(&self) -> Option<ViewId>;
 688//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
 689//     fn from_state_proto(
 690//         pane: ViewHandle<Pane>,
 691//         project: ViewHandle<Workspace>,
 692//         id: ViewId,
 693//         state: &mut Option<proto::view::Variant>,
 694//         cx: &mut AppContext,
 695//     ) -> Option<Task<Result<ViewHandle<Self>>>>;
 696//     fn add_event_to_update_proto(
 697//         &self,
 698//         event: &Self::Event,
 699//         update: &mut Option<proto::update_view::Variant>,
 700//         cx: &AppContext,
 701//     ) -> bool;
 702//     fn apply_update_proto(
 703//         &mut self,
 704//         project: &ModelHandle<Project>,
 705//         message: proto::update_view::Variant,
 706//         cx: &mut ViewContext<Self>,
 707//     ) -> Task<Result<()>>;
 708//     fn is_project_item(&self, cx: &AppContext) -> bool;
 709
 710//     fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
 711//     fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool;
 712// }
 713
 714// pub trait FollowableItemHandle: ItemHandle {
 715//     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId>;
 716//     fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext);
 717//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
 718//     fn add_event_to_update_proto(
 719//         &self,
 720//         event: &dyn Any,
 721//         update: &mut Option<proto::update_view::Variant>,
 722//         cx: &AppContext,
 723//     ) -> bool;
 724//     fn apply_update_proto(
 725//         &self,
 726//         project: &ModelHandle<Project>,
 727//         message: proto::update_view::Variant,
 728//         cx: &mut WindowContext,
 729//     ) -> Task<Result<()>>;
 730//     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
 731//     fn is_project_item(&self, cx: &AppContext) -> bool;
 732// }
 733
 734// impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
 735//     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
 736//         self.read(cx).remote_id().or_else(|| {
 737//             client.peer_id().map(|creator| ViewId {
 738//                 creator,
 739//                 id: self.id() as u64,
 740//             })
 741//         })
 742//     }
 743
 744//     fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
 745//         self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
 746//     }
 747
 748//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
 749//         self.read(cx).to_state_proto(cx)
 750//     }
 751
 752//     fn add_event_to_update_proto(
 753//         &self,
 754//         event: &dyn Any,
 755//         update: &mut Option<proto::update_view::Variant>,
 756//         cx: &AppContext,
 757//     ) -> bool {
 758//         if let Some(event) = event.downcast_ref() {
 759//             self.read(cx).add_event_to_update_proto(event, update, cx)
 760//         } else {
 761//             false
 762//         }
 763//     }
 764
 765//     fn apply_update_proto(
 766//         &self,
 767//         project: &ModelHandle<Project>,
 768//         message: proto::update_view::Variant,
 769//         cx: &mut WindowContext,
 770//     ) -> Task<Result<()>> {
 771//         self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
 772//     }
 773
 774//     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
 775//         if let Some(event) = event.downcast_ref() {
 776//             T::should_unfollow_on_event(event, cx)
 777//         } else {
 778//             false
 779//         }
 780//     }
 781
 782//     fn is_project_item(&self, cx: &AppContext) -> bool {
 783//         self.read(cx).is_project_item(cx)
 784//     }
 785// }
 786
 787// #[cfg(any(test, feature = "test-support"))]
 788// pub mod test {
 789//     use super::{Item, ItemEvent};
 790//     use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
 791//     use gpui2::{
 792//         elements::Empty, AnyElement, AppContext, Element, Entity, ModelHandle, Task, View,
 793//         ViewContext, ViewHandle, WeakViewHandle,
 794//     };
 795//     use project2::{Project, ProjectEntryId, ProjectPath, WorktreeId};
 796//     use smallvec::SmallVec;
 797//     use std::{any::Any, borrow::Cow, cell::Cell, path::Path};
 798
 799//     pub struct TestProjectItem {
 800//         pub entry_id: Option<ProjectEntryId>,
 801//         pub project_path: Option<ProjectPath>,
 802//     }
 803
 804//     pub struct TestItem {
 805//         pub workspace_id: WorkspaceId,
 806//         pub state: String,
 807//         pub label: String,
 808//         pub save_count: usize,
 809//         pub save_as_count: usize,
 810//         pub reload_count: usize,
 811//         pub is_dirty: bool,
 812//         pub is_singleton: bool,
 813//         pub has_conflict: bool,
 814//         pub project_items: Vec<ModelHandle<TestProjectItem>>,
 815//         pub nav_history: Option<ItemNavHistory>,
 816//         pub tab_descriptions: Option<Vec<&'static str>>,
 817//         pub tab_detail: Cell<Option<usize>>,
 818//     }
 819
 820//     impl Entity for TestProjectItem {
 821//         type Event = ();
 822//     }
 823
 824//     impl project2::Item for TestProjectItem {
 825//         fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
 826//             self.entry_id
 827//         }
 828
 829//         fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
 830//             self.project_path.clone()
 831//         }
 832//     }
 833
 834//     pub enum TestItemEvent {
 835//         Edit,
 836//     }
 837
 838//     impl Clone for TestItem {
 839//         fn clone(&self) -> Self {
 840//             Self {
 841//                 state: self.state.clone(),
 842//                 label: self.label.clone(),
 843//                 save_count: self.save_count,
 844//                 save_as_count: self.save_as_count,
 845//                 reload_count: self.reload_count,
 846//                 is_dirty: self.is_dirty,
 847//                 is_singleton: self.is_singleton,
 848//                 has_conflict: self.has_conflict,
 849//                 project_items: self.project_items.clone(),
 850//                 nav_history: None,
 851//                 tab_descriptions: None,
 852//                 tab_detail: Default::default(),
 853//                 workspace_id: self.workspace_id,
 854//             }
 855//         }
 856//     }
 857
 858//     impl TestProjectItem {
 859//         pub fn new(id: u64, path: &str, cx: &mut AppContext) -> ModelHandle<Self> {
 860//             let entry_id = Some(ProjectEntryId::from_proto(id));
 861//             let project_path = Some(ProjectPath {
 862//                 worktree_id: WorktreeId::from_usize(0),
 863//                 path: Path::new(path).into(),
 864//             });
 865//             cx.add_model(|_| Self {
 866//                 entry_id,
 867//                 project_path,
 868//             })
 869//         }
 870
 871//         pub fn new_untitled(cx: &mut AppContext) -> ModelHandle<Self> {
 872//             cx.add_model(|_| Self {
 873//                 project_path: None,
 874//                 entry_id: None,
 875//             })
 876//         }
 877//     }
 878
 879//     impl TestItem {
 880//         pub fn new() -> Self {
 881//             Self {
 882//                 state: String::new(),
 883//                 label: String::new(),
 884//                 save_count: 0,
 885//                 save_as_count: 0,
 886//                 reload_count: 0,
 887//                 is_dirty: false,
 888//                 has_conflict: false,
 889//                 project_items: Vec::new(),
 890//                 is_singleton: true,
 891//                 nav_history: None,
 892//                 tab_descriptions: None,
 893//                 tab_detail: Default::default(),
 894//                 workspace_id: 0,
 895//             }
 896//         }
 897
 898//         pub fn new_deserialized(id: WorkspaceId) -> Self {
 899//             let mut this = Self::new();
 900//             this.workspace_id = id;
 901//             this
 902//         }
 903
 904//         pub fn with_label(mut self, state: &str) -> Self {
 905//             self.label = state.to_string();
 906//             self
 907//         }
 908
 909//         pub fn with_singleton(mut self, singleton: bool) -> Self {
 910//             self.is_singleton = singleton;
 911//             self
 912//         }
 913
 914//         pub fn with_dirty(mut self, dirty: bool) -> Self {
 915//             self.is_dirty = dirty;
 916//             self
 917//         }
 918
 919//         pub fn with_conflict(mut self, has_conflict: bool) -> Self {
 920//             self.has_conflict = has_conflict;
 921//             self
 922//         }
 923
 924//         pub fn with_project_items(mut self, items: &[ModelHandle<TestProjectItem>]) -> Self {
 925//             self.project_items.clear();
 926//             self.project_items.extend(items.iter().cloned());
 927//             self
 928//         }
 929
 930//         pub fn set_state(&mut self, state: String, cx: &mut ViewContext<Self>) {
 931//             self.push_to_nav_history(cx);
 932//             self.state = state;
 933//         }
 934
 935//         fn push_to_nav_history(&mut self, cx: &mut ViewContext<Self>) {
 936//             if let Some(history) = &mut self.nav_history {
 937//                 history.push(Some(Box::new(self.state.clone())), cx);
 938//             }
 939//         }
 940//     }
 941
 942//     impl Entity for TestItem {
 943//         type Event = TestItemEvent;
 944//     }
 945
 946//     impl View for TestItem {
 947//         fn ui_name() -> &'static str {
 948//             "TestItem"
 949//         }
 950
 951//         fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
 952//             Empty::new().into_any()
 953//         }
 954//     }
 955
 956//     impl Item for TestItem {
 957//         fn tab_description(&self, detail: usize, _: &AppContext) -> Option<Cow<str>> {
 958//             self.tab_descriptions.as_ref().and_then(|descriptions| {
 959//                 let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
 960//                 Some(description.into())
 961//             })
 962//         }
 963
 964//         fn tab_content<V: 'static>(
 965//             &self,
 966//             detail: Option<usize>,
 967//             _: &theme2::Tab,
 968//             _: &AppContext,
 969//         ) -> AnyElement<V> {
 970//             self.tab_detail.set(detail);
 971//             Empty::new().into_any()
 972//         }
 973
 974//         fn for_each_project_item(
 975//             &self,
 976//             cx: &AppContext,
 977//             f: &mut dyn FnMut(usize, &dyn project2::Item),
 978//         ) {
 979//             self.project_items
 980//                 .iter()
 981//                 .for_each(|item| f(item.id(), item.read(cx)))
 982//         }
 983
 984//         fn is_singleton(&self, _: &AppContext) -> bool {
 985//             self.is_singleton
 986//         }
 987
 988//         fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
 989//             self.nav_history = Some(history);
 990//         }
 991
 992//         fn navigate(&mut self, state: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
 993//             let state = *state.downcast::<String>().unwrap_or_default();
 994//             if state != self.state {
 995//                 self.state = state;
 996//                 true
 997//             } else {
 998//                 false
 999//             }
1000//         }
1001
1002//         fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
1003//             self.push_to_nav_history(cx);
1004//         }
1005
1006//         fn clone_on_split(
1007//             &self,
1008//             _workspace_id: WorkspaceId,
1009//             _: &mut ViewContext<Self>,
1010//         ) -> Option<Self>
1011//         where
1012//             Self: Sized,
1013//         {
1014//             Some(self.clone())
1015//         }
1016
1017//         fn is_dirty(&self, _: &AppContext) -> bool {
1018//             self.is_dirty
1019//         }
1020
1021//         fn has_conflict(&self, _: &AppContext) -> bool {
1022//             self.has_conflict
1023//         }
1024
1025//         fn can_save(&self, cx: &AppContext) -> bool {
1026//             !self.project_items.is_empty()
1027//                 && self
1028//                     .project_items
1029//                     .iter()
1030//                     .all(|item| item.read(cx).entry_id.is_some())
1031//         }
1032
1033//         fn save(
1034//             &mut self,
1035//             _: ModelHandle<Project>,
1036//             _: &mut ViewContext<Self>,
1037//         ) -> Task<anyhow::Result<()>> {
1038//             self.save_count += 1;
1039//             self.is_dirty = false;
1040//             Task::ready(Ok(()))
1041//         }
1042
1043//         fn save_as(
1044//             &mut self,
1045//             _: ModelHandle<Project>,
1046//             _: std::path::PathBuf,
1047//             _: &mut ViewContext<Self>,
1048//         ) -> Task<anyhow::Result<()>> {
1049//             self.save_as_count += 1;
1050//             self.is_dirty = false;
1051//             Task::ready(Ok(()))
1052//         }
1053
1054//         fn reload(
1055//             &mut self,
1056//             _: ModelHandle<Project>,
1057//             _: &mut ViewContext<Self>,
1058//         ) -> Task<anyhow::Result<()>> {
1059//             self.reload_count += 1;
1060//             self.is_dirty = false;
1061//             Task::ready(Ok(()))
1062//         }
1063
1064//         fn to_item_events(_: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
1065//             [ItemEvent::UpdateTab, ItemEvent::Edit].into()
1066//         }
1067
1068//         fn serialized_item_kind() -> Option<&'static str> {
1069//             Some("TestItem")
1070//         }
1071
1072//         fn deserialize(
1073//             _project: ModelHandle<Project>,
1074//             _workspace: WeakViewHandle<Workspace>,
1075//             workspace_id: WorkspaceId,
1076//             _item_id: ItemId,
1077//             cx: &mut ViewContext<Pane>,
1078//         ) -> Task<anyhow::Result<ViewHandle<Self>>> {
1079//             let view = cx.add_view(|_cx| Self::new_deserialized(workspace_id));
1080//             Task::Ready(Some(anyhow::Ok(view)))
1081//         }
1082//     }
1083// }