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