From 199be8241c5ab47d7d41b0b36402ca3133e1944d Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 24 Aug 2023 11:25:20 -0700 Subject: [PATCH] Add following into channel notes co-authored-by: max --- crates/collab_ui/src/channel_view.rs | 108 ++++++++++++++++++++++++++- crates/editor/src/editor_tests.rs | 4 +- crates/editor/src/items.rs | 3 +- crates/rpc/proto/zed.proto | 5 ++ crates/workspace/src/item.rs | 2 +- crates/workspace/src/workspace.rs | 23 +++--- 6 files changed, 128 insertions(+), 17 deletions(-) diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index af45eabe69..c13711b29c 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -1,27 +1,34 @@ use channel::channel_buffer::ChannelBuffer; +use client::proto; use clock::ReplicaId; use collections::HashMap; use editor::Editor; use gpui::{ actions, elements::{ChildView, Label}, - AnyElement, AppContext, Element, Entity, ModelHandle, View, ViewContext, ViewHandle, + AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Subscription, View, + ViewContext, ViewHandle, }; use language::Language; use project::Project; use std::sync::Arc; -use workspace::item::{Item, ItemHandle}; +use workspace::{ + item::{FollowableItem, Item, ItemHandle}, + register_followable_item, ViewId, +}; actions!(channel_view, [Deploy]); pub(crate) fn init(cx: &mut AppContext) { - // TODO + register_followable_item::(cx) } pub struct ChannelView { editor: ViewHandle, project: ModelHandle, channel_buffer: ModelHandle, + remote_id: Option, + _editor_event_subscription: Subscription, } impl ChannelView { @@ -34,14 +41,19 @@ impl ChannelView { let buffer = channel_buffer.read(cx).buffer(); buffer.update(cx, |buffer, cx| buffer.set_language(language, cx)); let editor = cx.add_view(|cx| Editor::for_buffer(buffer, None, cx)); + let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone())); + let this = Self { editor, project, channel_buffer, + remote_id: None, + _editor_event_subscription, }; let mapping = this.project_replica_ids_by_channel_buffer_replica_id(cx); this.editor .update(cx, |editor, cx| editor.set_replica_id_mapping(mapping, cx)); + this } @@ -82,9 +94,15 @@ impl View for ChannelView { "ChannelView" } - fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement { + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { ChildView::new(self.editor.as_any(), cx).into_any() } + + fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(self.editor.as_any()) + } + } } impl Item for ChannelView { @@ -104,3 +122,85 @@ impl Item for ChannelView { Label::new(channel_name, style.label.to_owned()).into_any() } } + +impl FollowableItem for ChannelView { + fn remote_id(&self) -> Option { + self.remote_id + } + + fn to_state_proto(&self, cx: &AppContext) -> Option { + self.channel_buffer.read(cx).channel(cx).map(|channel| { + proto::view::Variant::ChannelView(proto::view::ChannelView { + channel_id: channel.id, + }) + }) + } + + fn from_state_proto( + _: ViewHandle, + workspace: ViewHandle, + remote_id: workspace::ViewId, + state_proto: &mut Option, + cx: &mut AppContext, + ) -> Option>>> { + let Some(proto::view::Variant::ChannelView(_)) = state_proto else { return None }; + let Some(proto::view::Variant::ChannelView(state)) = state_proto.take() else { unreachable!() }; + + let channel_store = &workspace.read(cx).app_state().channel_store.clone(); + let open_channel_buffer = channel_store.update(cx, |store, cx| { + store.open_channel_buffer(state.channel_id, cx) + }); + let project = workspace.read(cx).project().to_owned(); + let language = workspace.read(cx).app_state().languages.clone(); + let get_markdown = language.language_for_name("Markdown"); + + Some(cx.spawn(|mut cx| async move { + let channel_buffer = open_channel_buffer.await?; + let markdown = get_markdown.await?; + + let this = workspace + .update(&mut cx, move |_, cx| { + cx.add_view(|cx| { + let mut this = Self::new(project, channel_buffer, Some(markdown), cx); + this.remote_id = Some(remote_id); + this + }) + }) + .ok_or_else(|| anyhow::anyhow!("workspace droppped"))?; + + Ok(this) + })) + } + + fn add_event_to_update_proto( + &self, + _: &Self::Event, + _: &mut Option, + _: &AppContext, + ) -> bool { + false + } + + fn apply_update_proto( + &mut self, + _: &ModelHandle, + _: proto::update_view::Variant, + _: &mut ViewContext, + ) -> gpui::Task> { + gpui::Task::ready(Ok(())) + } + + fn set_leader_replica_id( + &mut self, + leader_replica_id: Option, + cx: &mut ViewContext, + ) { + self.editor.update(cx, |editor, cx| { + editor.set_leader_replica_id(leader_replica_id, cx) + }) + } + + fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool { + Editor::should_unfollow_on_event(event, cx) + } +} diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index e031edf538..a2a561402f 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6384,7 +6384,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { .update(|cx| { Editor::from_state_proto( pane.clone(), - project.clone(), + workspace.clone(), ViewId { creator: Default::default(), id: 0, @@ -6479,7 +6479,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { .update(|cx| { Editor::from_state_proto( pane.clone(), - project.clone(), + workspace.clone(), ViewId { creator: Default::default(), id: 0, diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 668ea48203..657aae5ff9 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -49,11 +49,12 @@ impl FollowableItem for Editor { fn from_state_proto( pane: ViewHandle, - project: ModelHandle, + workspace: ViewHandle, remote_id: ViewId, state: &mut Option, cx: &mut AppContext, ) -> Option>>> { + let project = workspace.read(cx).project().to_owned(); let Some(proto::view::Variant::Editor(_)) = state else { return None }; let Some(proto::view::Variant::Editor(state)) = state.take() else { unreachable!() }; diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index b97feff06b..827468b280 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1120,6 +1120,7 @@ message View { oneof variant { Editor editor = 3; + ChannelView channel_view = 4; } message Editor { @@ -1132,6 +1133,10 @@ message View { float scroll_x = 7; float scroll_y = 8; } + + message ChannelView { + uint64 channel_id = 1; + } } message Collaborator { diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index a115e0f473..4b5b7a7931 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -674,7 +674,7 @@ pub trait FollowableItem: Item { fn to_state_proto(&self, cx: &AppContext) -> Option; fn from_state_proto( pane: ViewHandle, - project: ModelHandle, + project: ViewHandle, id: ViewId, state: &mut Option, cx: &mut AppContext, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a8354472aa..62bb7a82a2 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -345,7 +345,7 @@ pub fn register_project_item(cx: &mut AppContext) { type FollowableItemBuilder = fn( ViewHandle, - ModelHandle, + ViewHandle, ViewId, &mut Option, &mut AppContext, @@ -362,8 +362,8 @@ pub fn register_followable_item(cx: &mut AppContext) { builders.insert( TypeId::of::(), ( - |pane, project, id, state, cx| { - I::from_state_proto(pane, project, id, state, cx).map(|task| { + |pane, workspace, id, state, cx| { + I::from_state_proto(pane, workspace, id, state, cx).map(|task| { cx.foreground() .spawn(async move { Ok(Box::new(task.await?) as Box<_>) }) }) @@ -2848,7 +2848,13 @@ impl Workspace { views: Vec, cx: &mut AsyncAppContext, ) -> Result<()> { - let project = this.read_with(cx, |this, _| this.project.clone())?; + let this = this + .upgrade(cx) + .ok_or_else(|| anyhow!("workspace dropped"))?; + let project = this + .read_with(cx, |this, _| this.project.clone()) + .ok_or_else(|| anyhow!("window dropped"))?; + let replica_id = project .read_with(cx, |project, _| { project @@ -2874,12 +2880,11 @@ impl Workspace { let id = ViewId::from_proto(id.clone())?; let mut variant = view.variant.clone(); if variant.is_none() { - Err(anyhow!("missing variant"))?; + Err(anyhow!("missing view variant"))?; } for build_item in &item_builders { - let task = cx.update(|cx| { - build_item(pane.clone(), project.clone(), id, &mut variant, cx) - }); + let task = cx + .update(|cx| build_item(pane.clone(), this.clone(), id, &mut variant, cx)); if let Some(task) = task { item_tasks.push(task); leader_view_ids.push(id); @@ -2907,7 +2912,7 @@ impl Workspace { } Some(()) - })?; + }); } Ok(()) }