Replace zed::watch with postage::watch for settings

Nathan Sobo and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

zed/src/editor/buffer_view.rs | 29 ++++++---------
zed/src/file_finder.rs        |  9 ++--
zed/src/lib.rs                |  1 
zed/src/menus.rs              |  5 +-
zed/src/settings.rs           |  4 +-
zed/src/watch.rs              | 65 -------------------------------------
zed/src/workspace.rs          |  8 +--
zed/src/workspace/pane.rs     |  5 +-
8 files changed, 27 insertions(+), 99 deletions(-)

Detailed changes

zed/src/editor/buffer_view.rs 🔗

@@ -2,7 +2,7 @@ use super::{
     buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point,
     Selection, SelectionSetId, ToOffset, ToPoint,
 };
-use crate::{settings::Settings, watch, workspace, worktree::FileHandle};
+use crate::{settings::Settings, workspace, worktree::FileHandle};
 use anyhow::Result;
 use futures_core::future::LocalBoxFuture;
 use gpui::{
@@ -12,6 +12,7 @@ use gpui::{
 };
 use gpui::{geometry::vector::Vector2F, TextLayoutCache};
 use parking_lot::Mutex;
+use postage::watch;
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use smol::Timer;
@@ -288,19 +289,13 @@ impl BufferView {
         settings: watch::Receiver<Settings>,
         ctx: &mut ViewContext<Self>,
     ) -> Self {
-        settings.notify_view_on_change(ctx);
-
         if let Some(file) = file.as_ref() {
             file.observe_from_view(ctx, |_, _, ctx| ctx.emit(Event::FileHandleChanged));
         }
 
         ctx.observe_model(&buffer, Self::on_buffer_changed);
         ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
-        let display_map = DisplayMap::new(
-            buffer.clone(),
-            smol::block_on(settings.read()).tab_size,
-            ctx.as_ref(),
-        );
+        let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, ctx.as_ref());
 
         let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| {
             buffer.add_selection_set(
@@ -1924,31 +1919,31 @@ impl BufferView {
     }
 
     pub fn font_size(&self) -> f32 {
-        smol::block_on(self.settings.read()).buffer_font_size
+        self.settings.borrow().buffer_font_size
     }
 
     pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         let ascent = font_cache.metric(font_id, |m| m.ascent);
         font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
     }
 
     pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         let ascent = font_cache.metric(font_id, |m| m.descent);
         font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
     }
 
     pub fn line_height(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         font_cache.line_height(font_id, settings.buffer_font_size)
     }
 
     pub fn em_width(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         font_cache.em_width(font_id, settings.buffer_font_size)
     }
@@ -1960,7 +1955,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         app: &AppContext,
     ) -> Result<f32> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_size = settings.buffer_font_size;
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -1985,7 +1980,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         ctx: &AppContext,
     ) -> Result<Vec<Arc<text_layout::Line>>> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_size = settings.buffer_font_size;
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -2029,7 +2024,7 @@ impl BufferView {
             return Ok(Vec::new());
         }
 
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
         let font_size = settings.buffer_font_size;
@@ -2067,7 +2062,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         app: &AppContext,
     ) -> Result<Arc<text_layout::Line>> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
 

zed/src/file_finder.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     editor::{buffer_view, BufferView},
     settings::Settings,
-    util, watch,
+    util,
     workspace::Workspace,
     worktree::{match_paths, PathMatch, Worktree},
 };
@@ -14,6 +14,7 @@ use gpui::{
     AppContext, Axis, Border, Entity, MutableAppContext, View, ViewContext, ViewHandle,
     WeakViewHandle,
 };
+use postage::watch;
 use std::{
     cmp,
     path::Path,
@@ -105,7 +106,7 @@ impl View for FileFinder {
 impl FileFinder {
     fn render_matches(&self) -> ElementBox {
         if self.matches.is_empty() {
-            let settings = smol::block_on(self.settings.read());
+            let settings = self.settings.borrow();
             return Container::new(
                 Label::new(
                     "No matches".into(),
@@ -148,7 +149,7 @@ impl FileFinder {
     ) -> Option<ElementBox> {
         self.labels_for_match(path_match, app).map(
             |(file_name, file_name_positions, full_path, full_path_positions)| {
-                let settings = smol::block_on(self.settings.read());
+                let settings = self.settings.borrow();
                 let highlight_color = ColorU::from_u32(0x304ee2ff);
                 let bold = *Properties::new().weight(Weight::BOLD);
                 let mut container = Container::new(
@@ -292,8 +293,6 @@ impl FileFinder {
         let query_buffer = ctx.add_view(|ctx| BufferView::single_line(settings.clone(), ctx));
         ctx.subscribe_to_view(&query_buffer, Self::on_query_buffer_event);
 
-        settings.notify_view_on_change(ctx);
-
         Self {
             handle: ctx.handle().downgrade(),
             settings,

zed/src/lib.rs 🔗

@@ -9,6 +9,5 @@ mod sum_tree;
 mod test;
 mod time;
 mod util;
-pub mod watch;
 pub mod workspace;
 mod worktree;

zed/src/menus.rs 🔗

@@ -1,8 +1,9 @@
-use crate::{settings::Settings, watch::Receiver};
+use crate::settings::Settings;
 use gpui::{Menu, MenuItem};
+use postage::watch;
 
 #[cfg(target_os = "macos")]
-pub fn menus(settings: Receiver<Settings>) -> Vec<Menu<'static>> {
+pub fn menus(settings: watch::Receiver<Settings>) -> Vec<Menu<'static>> {
     vec![
         Menu {
             name: "Zed",

zed/src/settings.rs 🔗

@@ -1,6 +1,6 @@
-use crate::watch;
 use anyhow::Result;
 use gpui::font_cache::{FamilyId, FontCache};
+use postage::watch;
 
 #[derive(Clone)]
 pub struct Settings {
@@ -26,5 +26,5 @@ impl Settings {
 pub fn channel(
     font_cache: &FontCache,
 ) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
-    Ok(watch::channel(Settings::new(font_cache)?))
+    Ok(watch::channel_with(Settings::new(font_cache)?))
 }

zed/src/watch.rs 🔗

@@ -1,65 +0,0 @@
-// TODO: This implementation is actually broken in that it will only
-
-use gpui::{Entity, ModelContext, View, ViewContext};
-use smol::{channel, lock::RwLock};
-use std::ops::Deref;
-use std::sync::Arc;
-
-pub struct Sender<T> {
-    value: Arc<RwLock<T>>,
-    updated: channel::Sender<()>,
-}
-
-#[derive(Clone)]
-pub struct Receiver<T> {
-    value: Arc<RwLock<T>>,
-    updated: channel::Receiver<()>,
-}
-
-impl<T> Sender<T> {
-    pub async fn update<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
-        let result = f(&mut *self.value.write().await);
-        self.updated.send(()).await.unwrap();
-        result
-    }
-}
-
-impl<T> Receiver<T> {
-    pub async fn updated(&self) {
-        let _ = self.updated.recv().await;
-    }
-
-    pub async fn read<'a>(&'a self) -> impl 'a + Deref<Target = T> {
-        self.value.read().await
-    }
-}
-
-// TODO: These implementations are broken because they only handle a single update.
-impl<T: 'static + Clone> Receiver<T> {
-    pub fn notify_model_on_change<M: 'static + Entity>(&self, ctx: &mut ModelContext<M>) {
-        let watch = self.clone();
-        ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
-            ctx.notify()
-        })
-        .detach();
-    }
-
-    pub fn notify_view_on_change<V: 'static + View>(&self, ctx: &mut ViewContext<V>) {
-        let watch = self.clone();
-        ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
-            ctx.notify()
-        })
-        .detach();
-    }
-}
-
-pub fn channel<T>(value: T) -> (Sender<T>, Receiver<T>) {
-    let value = Arc::new(RwLock::new(value));
-    let (s, r) = channel::unbounded();
-    let sender = Sender {
-        value: value.clone(),
-        updated: s,
-    };
-    let receiver = Receiver { value, updated: r };
-    (sender, receiver)
-}

zed/src/workspace.rs 🔗

@@ -3,11 +3,9 @@ pub mod pane_group;
 pub use pane::*;
 pub use pane_group::*;
 
-use crate::{
-    settings::Settings,
-    watch::{self, Receiver},
-};
+use crate::settings::Settings;
 use gpui::{MutableAppContext, PathPromptOptions};
+use postage::watch;
 use std::path::PathBuf;
 pub fn init(app: &mut MutableAppContext) {
     app.add_global_action("workspace:open", open);
@@ -44,7 +42,7 @@ pub struct OpenParams {
     pub settings: watch::Receiver<Settings>,
 }
 
-fn open(settings: &Receiver<Settings>, ctx: &mut MutableAppContext) {
+fn open(settings: &watch::Receiver<Settings>, ctx: &mut MutableAppContext) {
     let settings = settings.clone();
     ctx.prompt_for_paths(
         PathPromptOptions {

zed/src/workspace/pane.rs 🔗

@@ -1,5 +1,5 @@
 use super::{ItemViewHandle, SplitDirection};
-use crate::{settings::Settings, watch};
+use crate::settings::Settings;
 use gpui::{
     color::ColorU,
     elements::*,
@@ -7,6 +7,7 @@ use gpui::{
     keymap::Binding,
     AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext,
 };
+use postage::watch;
 use std::{cmp, path::Path, sync::Arc};
 
 pub fn init(app: &mut MutableAppContext) {
@@ -185,7 +186,7 @@ impl Pane {
     }
 
     fn render_tabs(&self, ctx: &AppContext) -> ElementBox {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let border_color = ColorU::from_u32(0xdbdbdcff);
         let line_height = ctx.font_cache().line_height(
             ctx.font_cache().default_font(settings.ui_font_family),