@@ -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::<ChannelView>(cx)
}
pub struct ChannelView {
editor: ViewHandle<Editor>,
project: ModelHandle<Project>,
channel_buffer: ModelHandle<ChannelBuffer>,
+ remote_id: Option<ViewId>,
+ _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<Self> {
+ 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() {
+ 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<workspace::ViewId> {
+ self.remote_id
+ }
+
+ fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
+ 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::Pane>,
+ workspace: ViewHandle<workspace::Workspace>,
+ remote_id: workspace::ViewId,
+ state_proto: &mut Option<proto::view::Variant>,
+ cx: &mut AppContext,
+ ) -> Option<gpui::Task<anyhow::Result<ViewHandle<Self>>>> {
+ 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<proto::update_view::Variant>,
+ _: &AppContext,
+ ) -> bool {
+ false
+ }
+
+ fn apply_update_proto(
+ &mut self,
+ _: &ModelHandle<Project>,
+ _: proto::update_view::Variant,
+ _: &mut ViewContext<Self>,
+ ) -> gpui::Task<anyhow::Result<()>> {
+ gpui::Task::ready(Ok(()))
+ }
+
+ fn set_leader_replica_id(
+ &mut self,
+ leader_replica_id: Option<u16>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ 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)
+ }
+}
@@ -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,
@@ -345,7 +345,7 @@ pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {
type FollowableItemBuilder = fn(
ViewHandle<Pane>,
- ModelHandle<Project>,
+ ViewHandle<Workspace>,
ViewId,
&mut Option<proto::view::Variant>,
&mut AppContext,
@@ -362,8 +362,8 @@ pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) {
builders.insert(
TypeId::of::<I>(),
(
- |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<proto::View>,
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(())
}