Allow FollowableItem::to_state_message to return None

Max Brunsfeld created

This way, we can avoid a panic if we don't handle certain cases,
like a non-singleton editor.

Change summary

crates/editor/src/items.rs        | 14 ++++----------
crates/workspace/src/workspace.rs | 24 +++++++++++++-----------
2 files changed, 17 insertions(+), 21 deletions(-)

Detailed changes

crates/editor/src/items.rs 🔗

@@ -50,21 +50,15 @@ impl FollowableItem for Editor {
         }))
     }
 
-    fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant {
-        let buffer_id = self
-            .buffer
-            .read(cx)
-            .as_singleton()
-            .unwrap()
-            .read(cx)
-            .remote_id();
-        proto::view::Variant::Editor(proto::view::Editor {
+    fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant> {
+        let buffer_id = self.buffer.read(cx).as_singleton()?.read(cx).remote_id();
+        Some(proto::view::Variant::Editor(proto::view::Editor {
             buffer_id,
             scroll_top: self
                 .scroll_top_anchor
                 .as_ref()
                 .map(|anchor| language::proto::serialize_anchor(&anchor.text_anchor)),
-        })
+        }))
     }
 
     fn to_update_message(

crates/workspace/src/workspace.rs 🔗

@@ -247,7 +247,7 @@ pub trait FollowableItem: Item {
         state: &mut Option<proto::view::Variant>,
         cx: &mut MutableAppContext,
     ) -> Option<Task<Result<ViewHandle<Self>>>>;
-    fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant;
+    fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant>;
     fn to_update_message(
         &self,
         event: &Self::Event,
@@ -261,7 +261,7 @@ pub trait FollowableItem: Item {
 }
 
 pub trait FollowableItemHandle: ItemHandle {
-    fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant;
+    fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant>;
     fn to_update_message(
         &self,
         event: &dyn Any,
@@ -275,7 +275,7 @@ pub trait FollowableItemHandle: ItemHandle {
 }
 
 impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
-    fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant {
+    fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant> {
         self.read(cx).to_state_message(cx)
     }
 
@@ -381,13 +381,15 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         cx: &mut ViewContext<Workspace>,
     ) {
         if let Some(followed_item) = self.to_followable_item_handle(cx) {
-            workspace.update_followers(
-                proto::update_followers::Variant::CreateView(proto::View {
-                    id: followed_item.id() as u64,
-                    variant: Some(followed_item.to_state_message(cx)),
-                }),
-                cx,
-            );
+            if let Some(message) = followed_item.to_state_message(cx) {
+                workspace.update_followers(
+                    proto::update_followers::Variant::CreateView(proto::View {
+                        id: followed_item.id() as u64,
+                        variant: Some(message),
+                    }),
+                    cx,
+                );
+            }
         }
 
         let pane = pane.downgrade();
@@ -1523,7 +1525,7 @@ impl Workspace {
                     .filter_map(|item| {
                         let id = item.id() as u64;
                         let item = item.to_followable_item_handle(cx)?;
-                        let variant = item.to_state_message(cx);
+                        let variant = item.to_state_message(cx)?;
                         Some(proto::View {
                             id,
                             variant: Some(variant),