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