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