item.rs

   1use crate::{
   2    pane::{self, Pane},
   3    persistence::model::ItemId,
   4    searchable::SearchableItemHandle,
   5    workspace_settings::{AutosaveSetting, WorkspaceSettings},
   6    DelayedDebouncedEditAction, FollowableViewRegistry, ItemNavHistory, SerializableItemRegistry,
   7    ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
   8};
   9use anyhow::Result;
  10use client::{
  11    proto::{self, PeerId},
  12    Client,
  13};
  14use futures::{channel::mpsc, StreamExt};
  15use gpui::{
  16    AnyElement, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, FocusableView,
  17    Font, HighlightStyle, Model, Pixels, Point, SharedString, Task, View, ViewContext, WeakView,
  18    WindowContext,
  19};
  20use project::{Project, ProjectEntryId, ProjectPath};
  21use schemars::JsonSchema;
  22use serde::{Deserialize, Serialize};
  23use settings::{Settings, SettingsLocation, SettingsSources};
  24use smallvec::SmallVec;
  25use std::{
  26    any::{Any, TypeId},
  27    cell::RefCell,
  28    ops::Range,
  29    rc::Rc,
  30    sync::Arc,
  31    time::Duration,
  32};
  33use theme::Theme;
  34use ui::{Color, Element as _, Icon, IntoElement, Label, LabelCommon};
  35use util::ResultExt;
  36
  37pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200);
  38
  39#[derive(Clone, Serialize, Deserialize, JsonSchema)]
  40#[serde(default)]
  41pub struct ItemSettings {
  42    /// Whether to show the Git file status on a tab item.
  43    pub git_status: bool,
  44    /// Position of the close button in a tab.
  45    pub close_position: ClosePosition,
  46    /// Whether to show the file icon for a tab.
  47    pub file_icons: bool,
  48}
  49
  50impl Default for ItemSettings {
  51    fn default() -> Self {
  52        Self {
  53            git_status: false,
  54            close_position: ClosePosition::Right,
  55            file_icons: false,
  56        }
  57    }
  58}
  59
  60#[derive(Clone, Serialize, Deserialize, JsonSchema)]
  61#[serde(default)]
  62pub struct PreviewTabsSettings {
  63    /// Whether to show opened editors as preview tabs.
  64    /// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
  65    pub enabled: bool,
  66    /// Whether to open tabs in preview mode when selected from the file finder.
  67    pub enable_preview_from_file_finder: bool,
  68    /// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
  69    pub enable_preview_from_code_navigation: bool,
  70}
  71
  72impl Default for PreviewTabsSettings {
  73    fn default() -> Self {
  74        Self {
  75            enabled: true,
  76            enable_preview_from_file_finder: false,
  77            enable_preview_from_code_navigation: false,
  78        }
  79    }
  80}
  81
  82#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
  83#[serde(rename_all = "lowercase")]
  84pub enum ClosePosition {
  85    Left,
  86    #[default]
  87    Right,
  88}
  89
  90impl ClosePosition {
  91    pub fn right(&self) -> bool {
  92        match self {
  93            ClosePosition::Left => false,
  94            ClosePosition::Right => true,
  95        }
  96    }
  97}
  98
  99impl Settings for ItemSettings {
 100    const KEY: Option<&'static str> = Some("tabs");
 101
 102    type FileContent = Self;
 103
 104    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
 105        sources.json_merge()
 106    }
 107}
 108
 109impl Settings for PreviewTabsSettings {
 110    const KEY: Option<&'static str> = Some("preview_tabs");
 111
 112    type FileContent = Self;
 113
 114    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
 115        sources.json_merge()
 116    }
 117}
 118
 119#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
 120pub enum ItemEvent {
 121    CloseItem,
 122    UpdateTab,
 123    UpdateBreadcrumbs,
 124    Edit,
 125}
 126
 127// TODO: Combine this with existing HighlightedText struct?
 128pub struct BreadcrumbText {
 129    pub text: String,
 130    pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
 131    pub font: Option<Font>,
 132}
 133
 134#[derive(Debug, Clone, Copy)]
 135pub struct TabContentParams {
 136    pub detail: Option<usize>,
 137    pub selected: bool,
 138    pub preview: bool,
 139}
 140
 141impl TabContentParams {
 142    /// Returns the text color to be used for the tab content.
 143    pub fn text_color(&self) -> Color {
 144        if self.selected {
 145            Color::Default
 146        } else {
 147            Color::Muted
 148        }
 149    }
 150}
 151
 152pub trait Item: FocusableView + EventEmitter<Self::Event> {
 153    type Event;
 154
 155    /// Returns the tab contents.
 156    ///
 157    /// By default this returns a [`Label`] that displays that text from
 158    /// `tab_content_text`.
 159    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
 160        let Some(text) = self.tab_content_text(cx) else {
 161            return gpui::Empty.into_any();
 162        };
 163
 164        Label::new(text)
 165            .color(params.text_color())
 166            .into_any_element()
 167    }
 168
 169    /// Returns the textual contents of the tab.
 170    ///
 171    /// Use this if you don't need to customize the tab contents.
 172    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
 173        None
 174    }
 175
 176    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
 177        None
 178    }
 179
 180    fn to_item_events(_event: &Self::Event, _f: impl FnMut(ItemEvent)) {}
 181
 182    fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
 183    fn discarded(&self, _project: Model<Project>, _cx: &mut ViewContext<Self>) {}
 184    fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
 185    fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
 186        false
 187    }
 188    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
 189        None
 190    }
 191    fn tab_description(&self, _: usize, _: &AppContext) -> Option<SharedString> {
 192        None
 193    }
 194
 195    fn telemetry_event_text(&self) -> Option<&'static str> {
 196        None
 197    }
 198
 199    /// (model id, Item)
 200    fn for_each_project_item(
 201        &self,
 202        _: &AppContext,
 203        _: &mut dyn FnMut(EntityId, &dyn project::Item),
 204    ) {
 205    }
 206    fn is_singleton(&self, _cx: &AppContext) -> bool {
 207        false
 208    }
 209    fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
 210    fn clone_on_split(
 211        &self,
 212        _workspace_id: Option<WorkspaceId>,
 213        _: &mut ViewContext<Self>,
 214    ) -> Option<View<Self>>
 215    where
 216        Self: Sized,
 217    {
 218        None
 219    }
 220    fn is_dirty(&self, _: &AppContext) -> bool {
 221        false
 222    }
 223    fn has_conflict(&self, _: &AppContext) -> bool {
 224        false
 225    }
 226    fn can_save(&self, _cx: &AppContext) -> bool {
 227        false
 228    }
 229    fn save(
 230        &mut self,
 231        _format: bool,
 232        _project: Model<Project>,
 233        _cx: &mut ViewContext<Self>,
 234    ) -> Task<Result<()>> {
 235        unimplemented!("save() must be implemented if can_save() returns true")
 236    }
 237    fn save_as(
 238        &mut self,
 239        _project: Model<Project>,
 240        _path: ProjectPath,
 241        _cx: &mut ViewContext<Self>,
 242    ) -> Task<Result<()>> {
 243        unimplemented!("save_as() must be implemented if can_save() returns true")
 244    }
 245    fn reload(
 246        &mut self,
 247        _project: Model<Project>,
 248        _cx: &mut ViewContext<Self>,
 249    ) -> Task<Result<()>> {
 250        unimplemented!("reload() must be implemented if can_save() returns true")
 251    }
 252
 253    fn act_as_type<'a>(
 254        &'a self,
 255        type_id: TypeId,
 256        self_handle: &'a View<Self>,
 257        _: &'a AppContext,
 258    ) -> Option<AnyView> {
 259        if TypeId::of::<Self>() == type_id {
 260            Some(self_handle.clone().into())
 261        } else {
 262            None
 263        }
 264    }
 265
 266    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
 267        None
 268    }
 269
 270    fn breadcrumb_location(&self) -> ToolbarItemLocation {
 271        ToolbarItemLocation::Hidden
 272    }
 273
 274    fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
 275        None
 276    }
 277
 278    fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
 279
 280    fn show_toolbar(&self) -> bool {
 281        true
 282    }
 283
 284    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
 285        None
 286    }
 287
 288    fn preserve_preview(&self, _cx: &AppContext) -> bool {
 289        false
 290    }
 291}
 292
 293pub trait SerializableItem: Item {
 294    fn serialized_item_kind() -> &'static str;
 295
 296    fn cleanup(
 297        workspace_id: WorkspaceId,
 298        alive_items: Vec<ItemId>,
 299        cx: &mut WindowContext,
 300    ) -> Task<Result<()>>;
 301
 302    fn deserialize(
 303        _project: Model<Project>,
 304        _workspace: WeakView<Workspace>,
 305        _workspace_id: WorkspaceId,
 306        _item_id: ItemId,
 307        _cx: &mut ViewContext<Pane>,
 308    ) -> Task<Result<View<Self>>>;
 309
 310    fn serialize(
 311        &mut self,
 312        workspace: &mut Workspace,
 313        item_id: ItemId,
 314        closing: bool,
 315        cx: &mut ViewContext<Self>,
 316    ) -> Option<Task<Result<()>>>;
 317
 318    fn should_serialize(&self, event: &Self::Event) -> bool;
 319}
 320
 321pub trait SerializableItemHandle: ItemHandle {
 322    fn serialized_item_kind(&self) -> &'static str;
 323    fn serialize(
 324        &self,
 325        workspace: &mut Workspace,
 326        closing: bool,
 327        cx: &mut WindowContext,
 328    ) -> Option<Task<Result<()>>>;
 329    fn should_serialize(&self, event: &dyn Any, cx: &AppContext) -> bool;
 330}
 331
 332impl<T> SerializableItemHandle for View<T>
 333where
 334    T: SerializableItem,
 335{
 336    fn serialized_item_kind(&self) -> &'static str {
 337        T::serialized_item_kind()
 338    }
 339
 340    fn serialize(
 341        &self,
 342        workspace: &mut Workspace,
 343        closing: bool,
 344        cx: &mut WindowContext,
 345    ) -> Option<Task<Result<()>>> {
 346        self.update(cx, |this, cx| {
 347            this.serialize(workspace, cx.entity_id().as_u64(), closing, cx)
 348        })
 349    }
 350
 351    fn should_serialize(&self, event: &dyn Any, cx: &AppContext) -> bool {
 352        event
 353            .downcast_ref::<T::Event>()
 354            .map_or(false, |event| self.read(cx).should_serialize(event))
 355    }
 356}
 357
 358pub trait ItemHandle: 'static + Send {
 359    fn subscribe_to_item_events(
 360        &self,
 361        cx: &mut WindowContext,
 362        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
 363    ) -> gpui::Subscription;
 364    fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
 365    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
 366    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
 367    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement;
 368    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon>;
 369    fn telemetry_event_text(&self, cx: &WindowContext) -> Option<&'static str>;
 370    fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement;
 371    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 372    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
 373    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]>;
 374    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]>;
 375    fn for_each_project_item(
 376        &self,
 377        _: &AppContext,
 378        _: &mut dyn FnMut(EntityId, &dyn project::Item),
 379    );
 380    fn is_singleton(&self, cx: &AppContext) -> bool;
 381    fn boxed_clone(&self) -> Box<dyn ItemHandle>;
 382    fn clone_on_split(
 383        &self,
 384        workspace_id: Option<WorkspaceId>,
 385        cx: &mut WindowContext,
 386    ) -> Option<Box<dyn ItemHandle>>;
 387    fn added_to_pane(
 388        &self,
 389        workspace: &mut Workspace,
 390        pane: View<Pane>,
 391        cx: &mut ViewContext<Workspace>,
 392    );
 393    fn deactivated(&self, cx: &mut WindowContext);
 394    fn discarded(&self, project: Model<Project>, cx: &mut WindowContext);
 395    fn workspace_deactivated(&self, cx: &mut WindowContext);
 396    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
 397    fn item_id(&self) -> EntityId;
 398    fn to_any(&self) -> AnyView;
 399    fn is_dirty(&self, cx: &AppContext) -> bool;
 400    fn has_conflict(&self, cx: &AppContext) -> bool;
 401    fn can_save(&self, cx: &AppContext) -> bool;
 402    fn save(
 403        &self,
 404        format: bool,
 405        project: Model<Project>,
 406        cx: &mut WindowContext,
 407    ) -> Task<Result<()>>;
 408    fn save_as(
 409        &self,
 410        project: Model<Project>,
 411        path: ProjectPath,
 412        cx: &mut WindowContext,
 413    ) -> Task<Result<()>>;
 414    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
 415    fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyView>;
 416    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
 417    fn to_serializable_item_handle(
 418        &self,
 419        cx: &AppContext,
 420    ) -> Option<Box<dyn SerializableItemHandle>>;
 421    fn on_release(
 422        &self,
 423        cx: &mut AppContext,
 424        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
 425    ) -> gpui::Subscription;
 426    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
 427    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
 428    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
 429    fn show_toolbar(&self, cx: &AppContext) -> bool;
 430    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
 431    fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
 432    fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings;
 433    fn preserve_preview(&self, cx: &AppContext) -> bool;
 434}
 435
 436pub trait WeakItemHandle: Send + Sync {
 437    fn id(&self) -> EntityId;
 438    fn boxed_clone(&self) -> Box<dyn WeakItemHandle>;
 439    fn upgrade(&self) -> Option<Box<dyn ItemHandle>>;
 440}
 441
 442impl dyn ItemHandle {
 443    pub fn downcast<V: 'static>(&self) -> Option<View<V>> {
 444        self.to_any().downcast().ok()
 445    }
 446
 447    pub fn act_as<V: 'static>(&self, cx: &AppContext) -> Option<View<V>> {
 448        self.act_as_type(TypeId::of::<V>(), cx)
 449            .and_then(|t| t.downcast().ok())
 450    }
 451}
 452
 453impl<T: Item> ItemHandle for View<T> {
 454    fn subscribe_to_item_events(
 455        &self,
 456        cx: &mut WindowContext,
 457        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
 458    ) -> gpui::Subscription {
 459        cx.subscribe(self, move |_, event, cx| {
 460            T::to_item_events(event, |item_event| handler(item_event, cx));
 461        })
 462    }
 463
 464    fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
 465        self.focus_handle(cx)
 466    }
 467
 468    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
 469        self.read(cx).tab_tooltip_text(cx)
 470    }
 471
 472    fn telemetry_event_text(&self, cx: &WindowContext) -> Option<&'static str> {
 473        self.read(cx).telemetry_event_text()
 474    }
 475
 476    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString> {
 477        self.read(cx).tab_description(detail, cx)
 478    }
 479
 480    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
 481        self.read(cx).tab_content(params, cx)
 482    }
 483
 484    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
 485        self.read(cx).tab_icon(cx)
 486    }
 487
 488    fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
 489        self.read(cx).tab_content(
 490            TabContentParams {
 491                selected: true,
 492                ..params
 493            },
 494            cx,
 495        )
 496    }
 497
 498    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
 499        let this = self.read(cx);
 500        let mut result = None;
 501        if this.is_singleton(cx) {
 502            this.for_each_project_item(cx, &mut |_, item| {
 503                result = item.project_path(cx);
 504            });
 505        }
 506        result
 507    }
 508
 509    fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings {
 510        if let Some(project_path) = self.project_path(cx) {
 511            WorkspaceSettings::get(
 512                Some(SettingsLocation {
 513                    worktree_id: project_path.worktree_id,
 514                    path: &project_path.path,
 515                }),
 516                cx,
 517            )
 518        } else {
 519            WorkspaceSettings::get_global(cx)
 520        }
 521    }
 522
 523    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> {
 524        let mut result = SmallVec::new();
 525        self.read(cx).for_each_project_item(cx, &mut |_, item| {
 526            if let Some(id) = item.entry_id(cx) {
 527                result.push(id);
 528            }
 529        });
 530        result
 531    }
 532
 533    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]> {
 534        let mut result = SmallVec::new();
 535        self.read(cx).for_each_project_item(cx, &mut |_, item| {
 536            if let Some(id) = item.project_path(cx) {
 537                result.push(id);
 538            }
 539        });
 540        result
 541    }
 542
 543    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]> {
 544        let mut result = SmallVec::new();
 545        self.read(cx).for_each_project_item(cx, &mut |id, _| {
 546            result.push(id);
 547        });
 548        result
 549    }
 550
 551    fn for_each_project_item(
 552        &self,
 553        cx: &AppContext,
 554        f: &mut dyn FnMut(EntityId, &dyn project::Item),
 555    ) {
 556        self.read(cx).for_each_project_item(cx, f)
 557    }
 558
 559    fn is_singleton(&self, cx: &AppContext) -> bool {
 560        self.read(cx).is_singleton(cx)
 561    }
 562
 563    fn boxed_clone(&self) -> Box<dyn ItemHandle> {
 564        Box::new(self.clone())
 565    }
 566
 567    fn clone_on_split(
 568        &self,
 569        workspace_id: Option<WorkspaceId>,
 570        cx: &mut WindowContext,
 571    ) -> Option<Box<dyn ItemHandle>> {
 572        self.update(cx, |item, cx| item.clone_on_split(workspace_id, cx))
 573            .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
 574    }
 575
 576    fn added_to_pane(
 577        &self,
 578        workspace: &mut Workspace,
 579        pane: View<Pane>,
 580        cx: &mut ViewContext<Workspace>,
 581    ) {
 582        let weak_item = self.downgrade();
 583        let history = pane.read(cx).nav_history_for_item(self);
 584        self.update(cx, |this, cx| {
 585            this.set_nav_history(history, cx);
 586            this.added_to_workspace(workspace, cx);
 587        });
 588
 589        if let Some(serializable_item) = self.to_serializable_item_handle(cx) {
 590            workspace
 591                .enqueue_item_serialization(serializable_item)
 592                .log_err();
 593        }
 594
 595        if workspace
 596            .panes_by_item
 597            .insert(self.item_id(), pane.downgrade())
 598            .is_none()
 599        {
 600            let mut pending_autosave = DelayedDebouncedEditAction::new();
 601            let (pending_update_tx, mut pending_update_rx) = mpsc::unbounded();
 602            let pending_update = Rc::new(RefCell::new(None));
 603
 604            let mut send_follower_updates = None;
 605            if let Some(item) = self.to_followable_item_handle(cx) {
 606                let is_project_item = item.is_project_item(cx);
 607                let item = item.downgrade();
 608
 609                send_follower_updates = Some(cx.spawn({
 610                    let pending_update = pending_update.clone();
 611                    |workspace, mut cx| async move {
 612                        while let Some(mut leader_id) = pending_update_rx.next().await {
 613                            while let Ok(Some(id)) = pending_update_rx.try_next() {
 614                                leader_id = id;
 615                            }
 616
 617                            workspace.update(&mut cx, |workspace, cx| {
 618                                let Some(item) = item.upgrade() else { return };
 619                                workspace.update_followers(
 620                                    is_project_item,
 621                                    proto::update_followers::Variant::UpdateView(
 622                                        proto::UpdateView {
 623                                            id: item
 624                                                .remote_id(workspace.client(), cx)
 625                                                .map(|id| id.to_proto()),
 626                                            variant: pending_update.borrow_mut().take(),
 627                                            leader_id,
 628                                        },
 629                                    ),
 630                                    cx,
 631                                );
 632                            })?;
 633                            cx.background_executor().timer(LEADER_UPDATE_THROTTLE).await;
 634                        }
 635                        anyhow::Ok(())
 636                    }
 637                }));
 638            }
 639
 640            let mut event_subscription = Some(cx.subscribe(
 641                self,
 642                move |workspace, item: View<T>, event, cx| {
 643                    let pane = if let Some(pane) = workspace
 644                        .panes_by_item
 645                        .get(&item.item_id())
 646                        .and_then(|pane| pane.upgrade())
 647                    {
 648                        pane
 649                    } else {
 650                        return;
 651                    };
 652
 653                    if let Some(item) = item.to_followable_item_handle(cx) {
 654                        let leader_id = workspace.leader_for_pane(&pane);
 655
 656                        if let Some(leader_id) = leader_id {
 657                            if let Some(FollowEvent::Unfollow) = item.to_follow_event(event) {
 658                                workspace.unfollow(leader_id, cx);
 659                            }
 660                        }
 661
 662                        if item.focus_handle(cx).contains_focused(cx) {
 663                            item.add_event_to_update_proto(
 664                                event,
 665                                &mut pending_update.borrow_mut(),
 666                                cx,
 667                            );
 668                            pending_update_tx.unbounded_send(leader_id).ok();
 669                        }
 670                    }
 671
 672                    if let Some(item) = item.to_serializable_item_handle(cx) {
 673                        if item.should_serialize(event, cx) {
 674                            workspace.enqueue_item_serialization(item).ok();
 675                        }
 676                    }
 677
 678                    T::to_item_events(event, |event| match event {
 679                        ItemEvent::CloseItem => {
 680                            pane.update(cx, |pane, cx| {
 681                                pane.close_item_by_id(item.item_id(), crate::SaveIntent::Close, cx)
 682                            })
 683                            .detach_and_log_err(cx);
 684                        }
 685
 686                        ItemEvent::UpdateTab => {
 687                            pane.update(cx, |_, cx| {
 688                                cx.emit(pane::Event::ChangeItemTitle);
 689                                cx.notify();
 690                            });
 691                        }
 692
 693                        ItemEvent::Edit => {
 694                            let autosave = item.workspace_settings(cx).autosave;
 695
 696                            if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
 697                                let delay = Duration::from_millis(milliseconds);
 698                                let item = item.clone();
 699                                pending_autosave.fire_new(delay, cx, move |workspace, cx| {
 700                                    Pane::autosave_item(&item, workspace.project().clone(), cx)
 701                                });
 702                            }
 703                            pane.update(cx, |pane, cx| pane.handle_item_edit(item.item_id(), cx));
 704                        }
 705
 706                        _ => {}
 707                    });
 708                },
 709            ));
 710
 711            cx.on_blur(&self.focus_handle(cx), move |workspace, cx| {
 712                if let Some(item) = weak_item.upgrade() {
 713                    if item.workspace_settings(cx).autosave == AutosaveSetting::OnFocusChange {
 714                        Pane::autosave_item(&item, workspace.project.clone(), cx)
 715                            .detach_and_log_err(cx);
 716                    }
 717                }
 718            })
 719            .detach();
 720
 721            let item_id = self.item_id();
 722            cx.observe_release(self, move |workspace, _, _| {
 723                workspace.panes_by_item.remove(&item_id);
 724                event_subscription.take();
 725                send_follower_updates.take();
 726            })
 727            .detach();
 728        }
 729
 730        cx.defer(|workspace, cx| {
 731            workspace.serialize_workspace(cx);
 732        });
 733    }
 734
 735    fn discarded(&self, project: Model<Project>, cx: &mut WindowContext) {
 736        self.update(cx, |this, cx| this.discarded(project, cx));
 737    }
 738
 739    fn deactivated(&self, cx: &mut WindowContext) {
 740        self.update(cx, |this, cx| this.deactivated(cx));
 741    }
 742
 743    fn workspace_deactivated(&self, cx: &mut WindowContext) {
 744        self.update(cx, |this, cx| this.workspace_deactivated(cx));
 745    }
 746
 747    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
 748        self.update(cx, |this, cx| this.navigate(data, cx))
 749    }
 750
 751    fn item_id(&self) -> EntityId {
 752        self.entity_id()
 753    }
 754
 755    fn to_any(&self) -> AnyView {
 756        self.clone().into()
 757    }
 758
 759    fn is_dirty(&self, cx: &AppContext) -> bool {
 760        self.read(cx).is_dirty(cx)
 761    }
 762
 763    fn has_conflict(&self, cx: &AppContext) -> bool {
 764        self.read(cx).has_conflict(cx)
 765    }
 766
 767    fn can_save(&self, cx: &AppContext) -> bool {
 768        self.read(cx).can_save(cx)
 769    }
 770
 771    fn save(
 772        &self,
 773        format: bool,
 774        project: Model<Project>,
 775        cx: &mut WindowContext,
 776    ) -> Task<Result<()>> {
 777        self.update(cx, |item, cx| item.save(format, project, cx))
 778    }
 779
 780    fn save_as(
 781        &self,
 782        project: Model<Project>,
 783        path: ProjectPath,
 784        cx: &mut WindowContext,
 785    ) -> Task<anyhow::Result<()>> {
 786        self.update(cx, |item, cx| item.save_as(project, path, cx))
 787    }
 788
 789    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
 790        self.update(cx, |item, cx| item.reload(project, cx))
 791    }
 792
 793    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<AnyView> {
 794        self.read(cx).act_as_type(type_id, self, cx)
 795    }
 796
 797    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
 798        FollowableViewRegistry::to_followable_view(self.clone(), cx)
 799    }
 800
 801    fn on_release(
 802        &self,
 803        cx: &mut AppContext,
 804        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
 805    ) -> gpui::Subscription {
 806        cx.observe_release(self, move |_, cx| callback(cx))
 807    }
 808
 809    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
 810        self.read(cx).as_searchable(self)
 811    }
 812
 813    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
 814        self.read(cx).breadcrumb_location()
 815    }
 816
 817    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
 818        self.read(cx).breadcrumbs(theme, cx)
 819    }
 820
 821    fn show_toolbar(&self, cx: &AppContext) -> bool {
 822        self.read(cx).show_toolbar()
 823    }
 824
 825    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
 826        self.read(cx).pixel_position_of_cursor(cx)
 827    }
 828
 829    fn downgrade_item(&self) -> Box<dyn WeakItemHandle> {
 830        Box::new(self.downgrade())
 831    }
 832
 833    fn to_serializable_item_handle(
 834        &self,
 835        cx: &AppContext,
 836    ) -> Option<Box<dyn SerializableItemHandle>> {
 837        SerializableItemRegistry::view_to_serializable_item_handle(self.to_any(), cx)
 838    }
 839
 840    fn preserve_preview(&self, cx: &AppContext) -> bool {
 841        self.read(cx).preserve_preview(cx)
 842    }
 843}
 844
 845impl From<Box<dyn ItemHandle>> for AnyView {
 846    fn from(val: Box<dyn ItemHandle>) -> Self {
 847        val.to_any()
 848    }
 849}
 850
 851impl From<&Box<dyn ItemHandle>> for AnyView {
 852    fn from(val: &Box<dyn ItemHandle>) -> Self {
 853        val.to_any()
 854    }
 855}
 856
 857impl Clone for Box<dyn ItemHandle> {
 858    fn clone(&self) -> Box<dyn ItemHandle> {
 859        self.boxed_clone()
 860    }
 861}
 862
 863impl<T: Item> WeakItemHandle for WeakView<T> {
 864    fn id(&self) -> EntityId {
 865        self.entity_id()
 866    }
 867
 868    fn boxed_clone(&self) -> Box<dyn WeakItemHandle> {
 869        Box::new(self.clone())
 870    }
 871
 872    fn upgrade(&self) -> Option<Box<dyn ItemHandle>> {
 873        self.upgrade().map(|v| Box::new(v) as Box<dyn ItemHandle>)
 874    }
 875}
 876
 877pub trait ProjectItem: Item {
 878    type Item: project::Item;
 879
 880    fn for_project_item(
 881        project: Model<Project>,
 882        item: Model<Self::Item>,
 883        cx: &mut ViewContext<Self>,
 884    ) -> Self
 885    where
 886        Self: Sized;
 887}
 888
 889#[derive(Debug)]
 890pub enum FollowEvent {
 891    Unfollow,
 892}
 893
 894pub enum Dedup {
 895    KeepExisting,
 896    ReplaceExisting,
 897}
 898
 899pub trait FollowableItem: Item {
 900    fn remote_id(&self) -> Option<ViewId>;
 901    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
 902    fn from_state_proto(
 903        project: View<Workspace>,
 904        id: ViewId,
 905        state: &mut Option<proto::view::Variant>,
 906        cx: &mut WindowContext,
 907    ) -> Option<Task<Result<View<Self>>>>;
 908    fn to_follow_event(event: &Self::Event) -> Option<FollowEvent>;
 909    fn add_event_to_update_proto(
 910        &self,
 911        event: &Self::Event,
 912        update: &mut Option<proto::update_view::Variant>,
 913        cx: &WindowContext,
 914    ) -> bool;
 915    fn apply_update_proto(
 916        &mut self,
 917        project: &Model<Project>,
 918        message: proto::update_view::Variant,
 919        cx: &mut ViewContext<Self>,
 920    ) -> Task<Result<()>>;
 921    fn is_project_item(&self, cx: &WindowContext) -> bool;
 922    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
 923    fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup>;
 924}
 925
 926pub trait FollowableItemHandle: ItemHandle {
 927    fn remote_id(&self, client: &Arc<Client>, cx: &WindowContext) -> Option<ViewId>;
 928    fn downgrade(&self) -> Box<dyn WeakFollowableItemHandle>;
 929    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext);
 930    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
 931    fn add_event_to_update_proto(
 932        &self,
 933        event: &dyn Any,
 934        update: &mut Option<proto::update_view::Variant>,
 935        cx: &WindowContext,
 936    ) -> bool;
 937    fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent>;
 938    fn apply_update_proto(
 939        &self,
 940        project: &Model<Project>,
 941        message: proto::update_view::Variant,
 942        cx: &mut WindowContext,
 943    ) -> Task<Result<()>>;
 944    fn is_project_item(&self, cx: &WindowContext) -> bool;
 945    fn dedup(&self, existing: &dyn FollowableItemHandle, cx: &WindowContext) -> Option<Dedup>;
 946}
 947
 948impl<T: FollowableItem> FollowableItemHandle for View<T> {
 949    fn remote_id(&self, client: &Arc<Client>, cx: &WindowContext) -> Option<ViewId> {
 950        self.read(cx).remote_id().or_else(|| {
 951            client.peer_id().map(|creator| ViewId {
 952                creator,
 953                id: self.item_id().as_u64(),
 954            })
 955        })
 956    }
 957
 958    fn downgrade(&self) -> Box<dyn WeakFollowableItemHandle> {
 959        Box::new(self.downgrade())
 960    }
 961
 962    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
 963        self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
 964    }
 965
 966    fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
 967        self.read(cx).to_state_proto(cx)
 968    }
 969
 970    fn add_event_to_update_proto(
 971        &self,
 972        event: &dyn Any,
 973        update: &mut Option<proto::update_view::Variant>,
 974        cx: &WindowContext,
 975    ) -> bool {
 976        if let Some(event) = event.downcast_ref() {
 977            self.read(cx).add_event_to_update_proto(event, update, cx)
 978        } else {
 979            false
 980        }
 981    }
 982
 983    fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent> {
 984        T::to_follow_event(event.downcast_ref()?)
 985    }
 986
 987    fn apply_update_proto(
 988        &self,
 989        project: &Model<Project>,
 990        message: proto::update_view::Variant,
 991        cx: &mut WindowContext,
 992    ) -> Task<Result<()>> {
 993        self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
 994    }
 995
 996    fn is_project_item(&self, cx: &WindowContext) -> bool {
 997        self.read(cx).is_project_item(cx)
 998    }
 999
1000    fn dedup(&self, existing: &dyn FollowableItemHandle, cx: &WindowContext) -> Option<Dedup> {
1001        let existing = existing.to_any().downcast::<T>().ok()?;
1002        self.read(cx).dedup(existing.read(cx), cx)
1003    }
1004}
1005
1006pub trait WeakFollowableItemHandle: Send + Sync {
1007    fn upgrade(&self) -> Option<Box<dyn FollowableItemHandle>>;
1008}
1009
1010impl<T: FollowableItem> WeakFollowableItemHandle for WeakView<T> {
1011    fn upgrade(&self) -> Option<Box<dyn FollowableItemHandle>> {
1012        Some(Box::new(self.upgrade()?))
1013    }
1014}
1015
1016#[cfg(any(test, feature = "test-support"))]
1017pub mod test {
1018    use super::{Item, ItemEvent, SerializableItem, TabContentParams};
1019    use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
1020    use gpui::{
1021        AnyElement, AppContext, Context as _, EntityId, EventEmitter, FocusableView,
1022        InteractiveElement, IntoElement, Model, Render, SharedString, Task, View, ViewContext,
1023        VisualContext, WeakView,
1024    };
1025    use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};
1026    use std::{any::Any, cell::Cell, path::Path};
1027
1028    pub struct TestProjectItem {
1029        pub entry_id: Option<ProjectEntryId>,
1030        pub project_path: Option<ProjectPath>,
1031    }
1032
1033    pub struct TestItem {
1034        pub workspace_id: Option<WorkspaceId>,
1035        pub state: String,
1036        pub label: String,
1037        pub save_count: usize,
1038        pub save_as_count: usize,
1039        pub reload_count: usize,
1040        pub is_dirty: bool,
1041        pub is_singleton: bool,
1042        pub has_conflict: bool,
1043        pub project_items: Vec<Model<TestProjectItem>>,
1044        pub nav_history: Option<ItemNavHistory>,
1045        pub tab_descriptions: Option<Vec<&'static str>>,
1046        pub tab_detail: Cell<Option<usize>>,
1047        serialize: Option<Box<dyn Fn() -> Option<Task<anyhow::Result<()>>>>>,
1048        focus_handle: gpui::FocusHandle,
1049    }
1050
1051    impl project::Item for TestProjectItem {
1052        fn try_open(
1053            _project: &Model<Project>,
1054            _path: &ProjectPath,
1055            _cx: &mut AppContext,
1056        ) -> Option<Task<gpui::Result<Model<Self>>>> {
1057            None
1058        }
1059
1060        fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
1061            self.entry_id
1062        }
1063
1064        fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
1065            self.project_path.clone()
1066        }
1067    }
1068
1069    pub enum TestItemEvent {
1070        Edit,
1071    }
1072
1073    impl TestProjectItem {
1074        pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model<Self> {
1075            let entry_id = Some(ProjectEntryId::from_proto(id));
1076            let project_path = Some(ProjectPath {
1077                worktree_id: WorktreeId::from_usize(0),
1078                path: Path::new(path).into(),
1079            });
1080            cx.new_model(|_| Self {
1081                entry_id,
1082                project_path,
1083            })
1084        }
1085
1086        pub fn new_untitled(cx: &mut AppContext) -> Model<Self> {
1087            cx.new_model(|_| Self {
1088                project_path: None,
1089                entry_id: None,
1090            })
1091        }
1092    }
1093
1094    impl TestItem {
1095        pub fn new(cx: &mut ViewContext<Self>) -> Self {
1096            Self {
1097                state: String::new(),
1098                label: String::new(),
1099                save_count: 0,
1100                save_as_count: 0,
1101                reload_count: 0,
1102                is_dirty: false,
1103                has_conflict: false,
1104                project_items: Vec::new(),
1105                is_singleton: true,
1106                nav_history: None,
1107                tab_descriptions: None,
1108                tab_detail: Default::default(),
1109                workspace_id: Default::default(),
1110                focus_handle: cx.focus_handle(),
1111                serialize: None,
1112            }
1113        }
1114
1115        pub fn new_deserialized(id: WorkspaceId, cx: &mut ViewContext<Self>) -> Self {
1116            let mut this = Self::new(cx);
1117            this.workspace_id = Some(id);
1118            this
1119        }
1120
1121        pub fn with_label(mut self, state: &str) -> Self {
1122            self.label = state.to_string();
1123            self
1124        }
1125
1126        pub fn with_singleton(mut self, singleton: bool) -> Self {
1127            self.is_singleton = singleton;
1128            self
1129        }
1130
1131        pub fn with_dirty(mut self, dirty: bool) -> Self {
1132            self.is_dirty = dirty;
1133            self
1134        }
1135
1136        pub fn with_conflict(mut self, has_conflict: bool) -> Self {
1137            self.has_conflict = has_conflict;
1138            self
1139        }
1140
1141        pub fn with_project_items(mut self, items: &[Model<TestProjectItem>]) -> Self {
1142            self.project_items.clear();
1143            self.project_items.extend(items.iter().cloned());
1144            self
1145        }
1146
1147        pub fn with_serialize(
1148            mut self,
1149            serialize: impl Fn() -> Option<Task<anyhow::Result<()>>> + 'static,
1150        ) -> Self {
1151            self.serialize = Some(Box::new(serialize));
1152            self
1153        }
1154
1155        pub fn set_state(&mut self, state: String, cx: &mut ViewContext<Self>) {
1156            self.push_to_nav_history(cx);
1157            self.state = state;
1158        }
1159
1160        fn push_to_nav_history(&mut self, cx: &mut ViewContext<Self>) {
1161            if let Some(history) = &mut self.nav_history {
1162                history.push(Some(Box::new(self.state.clone())), cx);
1163            }
1164        }
1165    }
1166
1167    impl Render for TestItem {
1168        fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
1169            gpui::div().track_focus(&self.focus_handle)
1170        }
1171    }
1172
1173    impl EventEmitter<ItemEvent> for TestItem {}
1174
1175    impl FocusableView for TestItem {
1176        fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
1177            self.focus_handle.clone()
1178        }
1179    }
1180
1181    impl Item for TestItem {
1182        type Event = ItemEvent;
1183
1184        fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
1185            f(*event)
1186        }
1187
1188        fn tab_description(&self, detail: usize, _: &AppContext) -> Option<SharedString> {
1189            self.tab_descriptions.as_ref().and_then(|descriptions| {
1190                let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
1191                Some(description.into())
1192            })
1193        }
1194
1195        fn telemetry_event_text(&self) -> Option<&'static str> {
1196            None
1197        }
1198
1199        fn tab_content(
1200            &self,
1201            params: TabContentParams,
1202            _cx: &ui::prelude::WindowContext,
1203        ) -> AnyElement {
1204            self.tab_detail.set(params.detail);
1205            gpui::div().into_any_element()
1206        }
1207
1208        fn for_each_project_item(
1209            &self,
1210            cx: &AppContext,
1211            f: &mut dyn FnMut(EntityId, &dyn project::Item),
1212        ) {
1213            self.project_items
1214                .iter()
1215                .for_each(|item| f(item.entity_id(), item.read(cx)))
1216        }
1217
1218        fn is_singleton(&self, _: &AppContext) -> bool {
1219            self.is_singleton
1220        }
1221
1222        fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
1223            self.nav_history = Some(history);
1224        }
1225
1226        fn navigate(&mut self, state: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
1227            let state = *state.downcast::<String>().unwrap_or_default();
1228            if state != self.state {
1229                self.state = state;
1230                true
1231            } else {
1232                false
1233            }
1234        }
1235
1236        fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
1237            self.push_to_nav_history(cx);
1238        }
1239
1240        fn clone_on_split(
1241            &self,
1242            _workspace_id: Option<WorkspaceId>,
1243            cx: &mut ViewContext<Self>,
1244        ) -> Option<View<Self>>
1245        where
1246            Self: Sized,
1247        {
1248            Some(cx.new_view(|cx| Self {
1249                state: self.state.clone(),
1250                label: self.label.clone(),
1251                save_count: self.save_count,
1252                save_as_count: self.save_as_count,
1253                reload_count: self.reload_count,
1254                is_dirty: self.is_dirty,
1255                is_singleton: self.is_singleton,
1256                has_conflict: self.has_conflict,
1257                project_items: self.project_items.clone(),
1258                nav_history: None,
1259                tab_descriptions: None,
1260                tab_detail: Default::default(),
1261                workspace_id: self.workspace_id,
1262                focus_handle: cx.focus_handle(),
1263                serialize: None,
1264            }))
1265        }
1266
1267        fn is_dirty(&self, _: &AppContext) -> bool {
1268            self.is_dirty
1269        }
1270
1271        fn has_conflict(&self, _: &AppContext) -> bool {
1272            self.has_conflict
1273        }
1274
1275        fn can_save(&self, cx: &AppContext) -> bool {
1276            !self.project_items.is_empty()
1277                && self
1278                    .project_items
1279                    .iter()
1280                    .all(|item| item.read(cx).entry_id.is_some())
1281        }
1282
1283        fn save(
1284            &mut self,
1285            _: bool,
1286            _: Model<Project>,
1287            _: &mut ViewContext<Self>,
1288        ) -> Task<anyhow::Result<()>> {
1289            self.save_count += 1;
1290            self.is_dirty = false;
1291            Task::ready(Ok(()))
1292        }
1293
1294        fn save_as(
1295            &mut self,
1296            _: Model<Project>,
1297            _: ProjectPath,
1298            _: &mut ViewContext<Self>,
1299        ) -> Task<anyhow::Result<()>> {
1300            self.save_as_count += 1;
1301            self.is_dirty = false;
1302            Task::ready(Ok(()))
1303        }
1304
1305        fn reload(
1306            &mut self,
1307            _: Model<Project>,
1308            _: &mut ViewContext<Self>,
1309        ) -> Task<anyhow::Result<()>> {
1310            self.reload_count += 1;
1311            self.is_dirty = false;
1312            Task::ready(Ok(()))
1313        }
1314    }
1315
1316    impl SerializableItem for TestItem {
1317        fn serialized_item_kind() -> &'static str {
1318            "TestItem"
1319        }
1320
1321        fn deserialize(
1322            _project: Model<Project>,
1323            _workspace: WeakView<Workspace>,
1324            workspace_id: WorkspaceId,
1325            _item_id: ItemId,
1326            cx: &mut ViewContext<Pane>,
1327        ) -> Task<anyhow::Result<View<Self>>> {
1328            let view = cx.new_view(|cx| Self::new_deserialized(workspace_id, cx));
1329            Task::ready(Ok(view))
1330        }
1331
1332        fn cleanup(
1333            _workspace_id: WorkspaceId,
1334            _alive_items: Vec<ItemId>,
1335            _cx: &mut ui::WindowContext,
1336        ) -> Task<anyhow::Result<()>> {
1337            Task::ready(Ok(()))
1338        }
1339
1340        fn serialize(
1341            &mut self,
1342            _workspace: &mut Workspace,
1343            _item_id: ItemId,
1344            _closing: bool,
1345            _cx: &mut ViewContext<Self>,
1346        ) -> Option<Task<anyhow::Result<()>>> {
1347            if let Some(serialize) = self.serialize.take() {
1348                let result = serialize();
1349                self.serialize = Some(serialize);
1350                result
1351            } else {
1352                None
1353            }
1354        }
1355
1356        fn should_serialize(&self, _event: &Self::Event) -> bool {
1357            false
1358        }
1359    }
1360}