@@ -1,454 +1,444 @@
-// use anyhow::{anyhow, Result};
-// use call::report_call_event_for_channel;
-// use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore};
-// use client::{
-// proto::{self, PeerId},
-// Collaborator, ParticipantIndex,
-// };
-// use collections::HashMap;
-// use editor::{CollaborationHub, Editor};
-// use gpui::{
-// actions,
-// elements::{ChildView, Label},
-// geometry::vector::Vector2F,
-// AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Subscription, Task, View,
-// ViewContext, ViewHandle,
-// };
-// use project::Project;
-// use smallvec::SmallVec;
-// use std::{
-// any::{Any, TypeId},
-// sync::Arc,
-// };
-// use util::ResultExt;
-// use workspace::{
-// item::{FollowableItem, Item, ItemEvent, ItemHandle},
-// register_followable_item,
-// searchable::SearchableItemHandle,
-// ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
-// };
-
-// actions!(channel_view, [Deploy]);
-
-// pub fn init(cx: &mut AppContext) {
-// register_followable_item::<ChannelView>(cx)
-// }
-
-// pub struct ChannelView {
-// pub editor: ViewHandle<Editor>,
-// project: ModelHandle<Project>,
-// channel_store: ModelHandle<ChannelStore>,
-// channel_buffer: ModelHandle<ChannelBuffer>,
-// remote_id: Option<ViewId>,
-// _editor_event_subscription: Subscription,
-// }
-
-// impl ChannelView {
-// pub fn open(
-// channel_id: ChannelId,
-// workspace: ViewHandle<Workspace>,
-// cx: &mut AppContext,
-// ) -> Task<Result<ViewHandle<Self>>> {
-// let pane = workspace.read(cx).active_pane().clone();
-// let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx);
-// cx.spawn(|mut cx| async move {
-// let channel_view = channel_view.await?;
-// pane.update(&mut cx, |pane, cx| {
-// report_call_event_for_channel(
-// "open channel notes",
-// channel_id,
-// &workspace.read(cx).app_state().client,
-// cx,
-// );
-// pane.add_item(Box::new(channel_view.clone()), true, true, None, cx);
-// });
-// anyhow::Ok(channel_view)
-// })
-// }
-
-// pub fn open_in_pane(
-// channel_id: ChannelId,
-// pane: ViewHandle<Pane>,
-// workspace: ViewHandle<Workspace>,
-// cx: &mut AppContext,
-// ) -> Task<Result<ViewHandle<Self>>> {
-// let workspace = workspace.read(cx);
-// let project = workspace.project().to_owned();
-// let channel_store = ChannelStore::global(cx);
-// let language_registry = workspace.app_state().languages.clone();
-// let markdown = language_registry.language_for_name("Markdown");
-// let channel_buffer =
-// channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx));
-
-// cx.spawn(|mut cx| async move {
-// let channel_buffer = channel_buffer.await?;
-// let markdown = markdown.await.log_err();
-
-// channel_buffer.update(&mut cx, |buffer, cx| {
-// buffer.buffer().update(cx, |buffer, cx| {
-// buffer.set_language_registry(language_registry);
-// if let Some(markdown) = markdown {
-// buffer.set_language(Some(markdown), cx);
-// }
-// })
-// });
-
-// pane.update(&mut cx, |pane, cx| {
-// let buffer_id = channel_buffer.read(cx).remote_id(cx);
-
-// let existing_view = pane
-// .items_of_type::<Self>()
-// .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id);
-
-// // If this channel buffer is already open in this pane, just return it.
-// if let Some(existing_view) = existing_view.clone() {
-// if existing_view.read(cx).channel_buffer == channel_buffer {
-// return existing_view;
-// }
-// }
-
-// let view = cx.add_view(|cx| {
-// let mut this = Self::new(project, channel_store, channel_buffer, cx);
-// this.acknowledge_buffer_version(cx);
-// this
-// });
-
-// // If the pane contained a disconnected view for this channel buffer,
-// // replace that.
-// if let Some(existing_item) = existing_view {
-// if let Some(ix) = pane.index_for_item(&existing_item) {
-// pane.close_item_by_id(existing_item.id(), SaveIntent::Skip, cx)
-// .detach();
-// pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx);
-// }
-// }
-
-// view
-// })
-// .ok_or_else(|| anyhow!("pane was dropped"))
-// })
-// }
-
-// pub fn new(
-// project: ModelHandle<Project>,
-// channel_store: ModelHandle<ChannelStore>,
-// channel_buffer: ModelHandle<ChannelBuffer>,
-// cx: &mut ViewContext<Self>,
-// ) -> Self {
-// let buffer = channel_buffer.read(cx).buffer();
-// let editor = cx.add_view(|cx| {
-// let mut editor = Editor::for_buffer(buffer, None, cx);
-// editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
-// channel_buffer.clone(),
-// )));
-// editor.set_read_only(
-// !channel_buffer
-// .read(cx)
-// .channel(cx)
-// .is_some_and(|c| c.can_edit_notes()),
-// );
-// editor
-// });
-// let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
-
-// cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event)
-// .detach();
-
-// Self {
-// editor,
-// project,
-// channel_store,
-// channel_buffer,
-// remote_id: None,
-// _editor_event_subscription,
-// }
-// }
-
-// pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
-// self.channel_buffer.read(cx).channel(cx)
-// }
-
-// fn handle_channel_buffer_event(
-// &mut self,
-// _: ModelHandle<ChannelBuffer>,
-// event: &ChannelBufferEvent,
-// cx: &mut ViewContext<Self>,
-// ) {
-// match event {
-// ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
-// editor.set_read_only(true);
-// cx.notify();
-// }),
-// ChannelBufferEvent::ChannelChanged => {
-// self.editor.update(cx, |editor, cx| {
-// editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
-// cx.emit(editor::Event::TitleChanged);
-// cx.notify()
-// });
-// }
-// ChannelBufferEvent::BufferEdited => {
-// if cx.is_self_focused() || self.editor.is_focused(cx) {
-// self.acknowledge_buffer_version(cx);
-// } else {
-// self.channel_store.update(cx, |store, cx| {
-// let channel_buffer = self.channel_buffer.read(cx);
-// store.notes_changed(
-// channel_buffer.channel_id,
-// channel_buffer.epoch(),
-// &channel_buffer.buffer().read(cx).version(),
-// cx,
-// )
-// });
-// }
-// }
-// ChannelBufferEvent::CollaboratorsChanged => {}
-// }
-// }
-
-// fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext<'_, '_, ChannelView>) {
-// self.channel_store.update(cx, |store, cx| {
-// let channel_buffer = self.channel_buffer.read(cx);
-// store.acknowledge_notes_version(
-// channel_buffer.channel_id,
-// channel_buffer.epoch(),
-// &channel_buffer.buffer().read(cx).version(),
-// cx,
-// )
-// });
-// self.channel_buffer.update(cx, |buffer, cx| {
-// buffer.acknowledge_buffer_version(cx);
-// });
-// }
-// }
-
-// impl Entity for ChannelView {
-// type Event = editor::Event;
-// }
-
-// impl View for ChannelView {
-// fn ui_name() -> &'static str {
-// "ChannelView"
-// }
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-// ChildView::new(self.editor.as_any(), cx).into_any()
-// }
-
-// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-// if cx.is_self_focused() {
-// self.acknowledge_buffer_version(cx);
-// cx.focus(self.editor.as_any())
-// }
-// }
-// }
-
-// impl Item for ChannelView {
-// fn act_as_type<'a>(
-// &'a self,
-// type_id: TypeId,
-// self_handle: &'a ViewHandle<Self>,
-// _: &'a AppContext,
-// ) -> Option<&'a AnyViewHandle> {
-// if type_id == TypeId::of::<Self>() {
-// Some(self_handle)
-// } else if type_id == TypeId::of::<Editor>() {
-// Some(&self.editor)
-// } else {
-// None
-// }
-// }
-
-// fn tab_content<V: 'static>(
-// &self,
-// _: Option<usize>,
-// style: &theme::Tab,
-// cx: &gpui::AppContext,
-// ) -> AnyElement<V> {
-// let label = if let Some(channel) = self.channel(cx) {
-// match (
-// channel.can_edit_notes(),
-// self.channel_buffer.read(cx).is_connected(),
-// ) {
-// (true, true) => format!("#{}", channel.name),
-// (false, true) => format!("#{} (read-only)", channel.name),
-// (_, false) => format!("#{} (disconnected)", channel.name),
-// }
-// } else {
-// format!("channel notes (disconnected)")
-// };
-// Label::new(label, style.label.to_owned()).into_any()
-// }
-
-// fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self> {
-// Some(Self::new(
-// self.project.clone(),
-// self.channel_store.clone(),
-// self.channel_buffer.clone(),
-// cx,
-// ))
-// }
-
-// fn is_singleton(&self, _cx: &AppContext) -> bool {
-// false
-// }
-
-// fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
-// self.editor
-// .update(cx, |editor, cx| editor.navigate(data, cx))
-// }
-
-// fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-// self.editor
-// .update(cx, |editor, cx| Item::deactivated(editor, cx))
-// }
-
-// fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
-// self.editor
-// .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx))
-// }
-
-// fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
-// Some(Box::new(self.editor.clone()))
-// }
-
-// fn show_toolbar(&self) -> bool {
-// true
-// }
-
-// fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
-// self.editor.read(cx).pixel_position_of_cursor(cx)
-// }
-
-// fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
-// editor::Editor::to_item_events(event)
-// }
-// }
-
-// impl FollowableItem for ChannelView {
-// fn remote_id(&self) -> Option<workspace::ViewId> {
-// self.remote_id
-// }
-
-// fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
-// let channel_buffer = self.channel_buffer.read(cx);
-// if !channel_buffer.is_connected() {
-// return None;
-// }
-
-// Some(proto::view::Variant::ChannelView(
-// proto::view::ChannelView {
-// channel_id: channel_buffer.channel_id,
-// editor: if let Some(proto::view::Variant::Editor(proto)) =
-// self.editor.read(cx).to_state_proto(cx)
-// {
-// Some(proto)
-// } else {
-// None
-// },
-// },
-// ))
-// }
-
-// fn from_state_proto(
-// pane: ViewHandle<workspace::Pane>,
-// workspace: ViewHandle<workspace::Workspace>,
-// remote_id: workspace::ViewId,
-// state: &mut Option<proto::view::Variant>,
-// cx: &mut AppContext,
-// ) -> Option<gpui::Task<anyhow::Result<ViewHandle<Self>>>> {
-// let Some(proto::view::Variant::ChannelView(_)) = state else {
-// return None;
-// };
-// let Some(proto::view::Variant::ChannelView(state)) = state.take() else {
-// unreachable!()
-// };
-
-// let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx);
-
-// Some(cx.spawn(|mut cx| async move {
-// let this = open.await?;
-
-// let task = this
-// .update(&mut cx, |this, cx| {
-// this.remote_id = Some(remote_id);
-
-// if let Some(state) = state.editor {
-// Some(this.editor.update(cx, |editor, cx| {
-// editor.apply_update_proto(
-// &this.project,
-// proto::update_view::Variant::Editor(proto::update_view::Editor {
-// selections: state.selections,
-// pending_selection: state.pending_selection,
-// scroll_top_anchor: state.scroll_top_anchor,
-// scroll_x: state.scroll_x,
-// scroll_y: state.scroll_y,
-// ..Default::default()
-// }),
-// cx,
-// )
-// }))
-// } else {
-// None
-// }
-// })
-// .ok_or_else(|| anyhow!("window was closed"))?;
-
-// if let Some(task) = task {
-// task.await?;
-// }
-
-// Ok(this)
-// }))
-// }
-
-// fn add_event_to_update_proto(
-// &self,
-// event: &Self::Event,
-// update: &mut Option<proto::update_view::Variant>,
-// cx: &AppContext,
-// ) -> bool {
-// self.editor
-// .read(cx)
-// .add_event_to_update_proto(event, update, cx)
-// }
-
-// fn apply_update_proto(
-// &mut self,
-// project: &ModelHandle<Project>,
-// message: proto::update_view::Variant,
-// cx: &mut ViewContext<Self>,
-// ) -> gpui::Task<anyhow::Result<()>> {
-// self.editor.update(cx, |editor, cx| {
-// editor.apply_update_proto(project, message, cx)
-// })
-// }
-
-// fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
-// self.editor.update(cx, |editor, cx| {
-// editor.set_leader_peer_id(leader_peer_id, cx)
-// })
-// }
-
-// fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool {
-// Editor::should_unfollow_on_event(event, cx)
-// }
-
-// fn is_project_item(&self, _cx: &AppContext) -> bool {
-// false
-// }
-// }
-
-// struct ChannelBufferCollaborationHub(ModelHandle<ChannelBuffer>);
-
-// impl CollaborationHub for ChannelBufferCollaborationHub {
-// fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
-// self.0.read(cx).collaborators()
-// }
-
-// fn user_participant_indices<'a>(
-// &self,
-// cx: &'a AppContext,
-// ) -> &'a HashMap<u64, ParticipantIndex> {
-// self.0.read(cx).user_store().read(cx).participant_indices()
-// }
-// }
+use anyhow::Result;
+use call::report_call_event_for_channel;
+use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore};
+use client::{
+ proto::{self, PeerId},
+ Collaborator, ParticipantIndex,
+};
+use collections::HashMap;
+use editor::{CollaborationHub, Editor, EditorEvent};
+use gpui::{
+ actions, AnyElement, AnyView, AppContext, Entity as _, EventEmitter, FocusableView,
+ IntoElement as _, Model, Pixels, Point, Render, Subscription, Task, View, ViewContext,
+ VisualContext as _, WindowContext,
+};
+use project::Project;
+use std::{
+ any::{Any, TypeId},
+ sync::Arc,
+};
+use ui::Label;
+use util::ResultExt;
+use workspace::{
+ item::{FollowableItem, Item, ItemEvent, ItemHandle},
+ register_followable_item,
+ searchable::SearchableItemHandle,
+ ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
+};
+
+actions!(Deploy);
+
+pub fn init(cx: &mut AppContext) {
+ register_followable_item::<ChannelView>(cx)
+}
+
+pub struct ChannelView {
+ pub editor: View<Editor>,
+ project: Model<Project>,
+ channel_store: Model<ChannelStore>,
+ channel_buffer: Model<ChannelBuffer>,
+ remote_id: Option<ViewId>,
+ _editor_event_subscription: Subscription,
+}
+
+impl ChannelView {
+ pub fn open(
+ channel_id: ChannelId,
+ workspace: View<Workspace>,
+ cx: &mut WindowContext,
+ ) -> Task<Result<View<Self>>> {
+ let pane = workspace.read(cx).active_pane().clone();
+ let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx);
+ cx.spawn(|mut cx| async move {
+ let channel_view = channel_view.await?;
+ pane.update(&mut cx, |pane, cx| {
+ report_call_event_for_channel(
+ "open channel notes",
+ channel_id,
+ &workspace.read(cx).app_state().client,
+ cx,
+ );
+ pane.add_item(Box::new(channel_view.clone()), true, true, None, cx);
+ })?;
+ anyhow::Ok(channel_view)
+ })
+ }
+
+ pub fn open_in_pane(
+ channel_id: ChannelId,
+ pane: View<Pane>,
+ workspace: View<Workspace>,
+ cx: &mut WindowContext,
+ ) -> Task<Result<View<Self>>> {
+ let workspace = workspace.read(cx);
+ let project = workspace.project().to_owned();
+ let channel_store = ChannelStore::global(cx);
+ let language_registry = workspace.app_state().languages.clone();
+ let markdown = language_registry.language_for_name("Markdown");
+ let channel_buffer =
+ channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx));
+
+ cx.spawn(|mut cx| async move {
+ let channel_buffer = channel_buffer.await?;
+ let markdown = markdown.await.log_err();
+
+ channel_buffer.update(&mut cx, |buffer, cx| {
+ buffer.buffer().update(cx, |buffer, cx| {
+ buffer.set_language_registry(language_registry);
+ if let Some(markdown) = markdown {
+ buffer.set_language(Some(markdown), cx);
+ }
+ })
+ })?;
+
+ pane.update(&mut cx, |pane, cx| {
+ let buffer_id = channel_buffer.read(cx).remote_id(cx);
+
+ let existing_view = pane
+ .items_of_type::<Self>()
+ .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id);
+
+ // If this channel buffer is already open in this pane, just return it.
+ if let Some(existing_view) = existing_view.clone() {
+ if existing_view.read(cx).channel_buffer == channel_buffer {
+ return existing_view;
+ }
+ }
+
+ let view = cx.build_view(|cx| {
+ let mut this = Self::new(project, channel_store, channel_buffer, cx);
+ this.acknowledge_buffer_version(cx);
+ this
+ });
+
+ // If the pane contained a disconnected view for this channel buffer,
+ // replace that.
+ if let Some(existing_item) = existing_view {
+ if let Some(ix) = pane.index_for_item(&existing_item) {
+ pane.close_item_by_id(existing_item.entity_id(), SaveIntent::Skip, cx)
+ .detach();
+ pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx);
+ }
+ }
+
+ view
+ })
+ })
+ }
+
+ pub fn new(
+ project: Model<Project>,
+ channel_store: Model<ChannelStore>,
+ channel_buffer: Model<ChannelBuffer>,
+ cx: &mut ViewContext<Self>,
+ ) -> Self {
+ let buffer = channel_buffer.read(cx).buffer();
+ let editor = cx.build_view(|cx| {
+ let mut editor = Editor::for_buffer(buffer, None, cx);
+ editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
+ channel_buffer.clone(),
+ )));
+ editor.set_read_only(
+ !channel_buffer
+ .read(cx)
+ .channel(cx)
+ .is_some_and(|c| c.can_edit_notes()),
+ );
+ editor
+ });
+ let _editor_event_subscription =
+ cx.subscribe(&editor, |_, _, e: &EditorEvent, cx| cx.emit(e.clone()));
+
+ cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event)
+ .detach();
+
+ Self {
+ editor,
+ project,
+ channel_store,
+ channel_buffer,
+ remote_id: None,
+ _editor_event_subscription,
+ }
+ }
+
+ pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+ self.channel_buffer.read(cx).channel(cx)
+ }
+
+ fn handle_channel_buffer_event(
+ &mut self,
+ _: Model<ChannelBuffer>,
+ event: &ChannelBufferEvent,
+ cx: &mut ViewContext<Self>,
+ ) {
+ match event {
+ ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
+ editor.set_read_only(true);
+ cx.notify();
+ }),
+ ChannelBufferEvent::ChannelChanged => {
+ self.editor.update(cx, |editor, cx| {
+ editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
+ cx.emit(editor::EditorEvent::TitleChanged);
+ cx.notify()
+ });
+ }
+ ChannelBufferEvent::BufferEdited => {
+ if self.editor.read(cx).is_focused(cx) {
+ self.acknowledge_buffer_version(cx);
+ } else {
+ self.channel_store.update(cx, |store, cx| {
+ let channel_buffer = self.channel_buffer.read(cx);
+ store.notes_changed(
+ channel_buffer.channel_id,
+ channel_buffer.epoch(),
+ &channel_buffer.buffer().read(cx).version(),
+ cx,
+ )
+ });
+ }
+ }
+ ChannelBufferEvent::CollaboratorsChanged => {}
+ }
+ }
+
+ fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext<ChannelView>) {
+ self.channel_store.update(cx, |store, cx| {
+ let channel_buffer = self.channel_buffer.read(cx);
+ store.acknowledge_notes_version(
+ channel_buffer.channel_id,
+ channel_buffer.epoch(),
+ &channel_buffer.buffer().read(cx).version(),
+ cx,
+ )
+ });
+ self.channel_buffer.update(cx, |buffer, cx| {
+ buffer.acknowledge_buffer_version(cx);
+ });
+ }
+}
+
+impl EventEmitter<EditorEvent> for ChannelView {}
+
+impl Render for ChannelView {
+ type Element = AnyView;
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ self.editor.clone().into()
+ }
+}
+
+impl FocusableView for ChannelView {
+ fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
+ self.editor.read(cx).focus_handle(cx)
+ }
+}
+
+impl Item for ChannelView {
+ type Event = EditorEvent;
+
+ fn act_as_type<'a>(
+ &'a self,
+ type_id: TypeId,
+ self_handle: &'a View<Self>,
+ _: &'a AppContext,
+ ) -> Option<AnyView> {
+ if type_id == TypeId::of::<Self>() {
+ Some(self_handle.to_any())
+ } else if type_id == TypeId::of::<Editor>() {
+ Some(self.editor.to_any())
+ } else {
+ None
+ }
+ }
+
+ fn tab_content(&self, _: Option<usize>, cx: &WindowContext) -> AnyElement {
+ let label = if let Some(channel) = self.channel(cx) {
+ match (
+ channel.can_edit_notes(),
+ self.channel_buffer.read(cx).is_connected(),
+ ) {
+ (true, true) => format!("#{}", channel.name),
+ (false, true) => format!("#{} (read-only)", channel.name),
+ (_, false) => format!("#{} (disconnected)", channel.name),
+ }
+ } else {
+ format!("channel notes (disconnected)")
+ };
+ Label::new(label).into_any_element()
+ }
+
+ fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<View<Self>> {
+ Some(cx.build_view(|cx| {
+ Self::new(
+ self.project.clone(),
+ self.channel_store.clone(),
+ self.channel_buffer.clone(),
+ cx,
+ )
+ }))
+ }
+
+ fn is_singleton(&self, _cx: &AppContext) -> bool {
+ false
+ }
+
+ fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
+ self.editor
+ .update(cx, |editor, cx| editor.navigate(data, cx))
+ }
+
+ fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+ self.editor
+ .update(cx, |editor, cx| Item::deactivated(editor, cx))
+ }
+
+ fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
+ self.editor
+ .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx))
+ }
+
+ fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+ Some(Box::new(self.editor.clone()))
+ }
+
+ fn show_toolbar(&self) -> bool {
+ true
+ }
+
+ fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
+ self.editor.read(cx).pixel_position_of_cursor(cx)
+ }
+
+ fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
+ Editor::to_item_events(event, f)
+ }
+}
+
+impl FollowableItem for ChannelView {
+ fn remote_id(&self) -> Option<workspace::ViewId> {
+ self.remote_id
+ }
+
+ fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
+ let channel_buffer = self.channel_buffer.read(cx);
+ if !channel_buffer.is_connected() {
+ return None;
+ }
+
+ Some(proto::view::Variant::ChannelView(
+ proto::view::ChannelView {
+ channel_id: channel_buffer.channel_id,
+ editor: if let Some(proto::view::Variant::Editor(proto)) =
+ self.editor.read(cx).to_state_proto(cx)
+ {
+ Some(proto)
+ } else {
+ None
+ },
+ },
+ ))
+ }
+
+ fn from_state_proto(
+ pane: View<workspace::Pane>,
+ workspace: View<workspace::Workspace>,
+ remote_id: workspace::ViewId,
+ state: &mut Option<proto::view::Variant>,
+ cx: &mut WindowContext,
+ ) -> Option<gpui::Task<anyhow::Result<View<Self>>>> {
+ let Some(proto::view::Variant::ChannelView(_)) = state else {
+ return None;
+ };
+ let Some(proto::view::Variant::ChannelView(state)) = state.take() else {
+ unreachable!()
+ };
+
+ let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx);
+
+ Some(cx.spawn(|mut cx| async move {
+ let this = open.await?;
+
+ let task = this.update(&mut cx, |this, cx| {
+ this.remote_id = Some(remote_id);
+
+ if let Some(state) = state.editor {
+ Some(this.editor.update(cx, |editor, cx| {
+ editor.apply_update_proto(
+ &this.project,
+ proto::update_view::Variant::Editor(proto::update_view::Editor {
+ selections: state.selections,
+ pending_selection: state.pending_selection,
+ scroll_top_anchor: state.scroll_top_anchor,
+ scroll_x: state.scroll_x,
+ scroll_y: state.scroll_y,
+ ..Default::default()
+ }),
+ cx,
+ )
+ }))
+ } else {
+ None
+ }
+ })?;
+
+ if let Some(task) = task {
+ task.await?;
+ }
+
+ Ok(this)
+ }))
+ }
+
+ fn add_event_to_update_proto(
+ &self,
+ event: &EditorEvent,
+ update: &mut Option<proto::update_view::Variant>,
+ cx: &WindowContext,
+ ) -> bool {
+ self.editor
+ .read(cx)
+ .add_event_to_update_proto(event, update, cx)
+ }
+
+ fn apply_update_proto(
+ &mut self,
+ project: &Model<Project>,
+ message: proto::update_view::Variant,
+ cx: &mut ViewContext<Self>,
+ ) -> gpui::Task<anyhow::Result<()>> {
+ self.editor.update(cx, |editor, cx| {
+ editor.apply_update_proto(project, message, cx)
+ })
+ }
+
+ fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
+ self.editor.update(cx, |editor, cx| {
+ editor.set_leader_peer_id(leader_peer_id, cx)
+ })
+ }
+
+ fn is_project_item(&self, _cx: &WindowContext) -> bool {
+ false
+ }
+
+ fn to_follow_event(event: &Self::Event) -> Option<workspace::item::FollowEvent> {
+ Editor::to_follow_event(event)
+ }
+}
+
+struct ChannelBufferCollaborationHub(Model<ChannelBuffer>);
+
+impl CollaborationHub for ChannelBufferCollaborationHub {
+ fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
+ self.0.read(cx).collaborators()
+ }
+
+ fn user_participant_indices<'a>(
+ &self,
+ cx: &'a AppContext,
+ ) -> &'a HashMap<u64, ParticipantIndex> {
+ self.0.read(cx).user_store().read(cx).participant_indices()
+ }
+}