WIP

Antonio Scandurra created

Change summary

crates/gpui2/src/app.rs                | 50 +++++++++++++++------------
crates/gpui2/src/app/async_context.rs  | 40 ++++++++++++++++++++-
crates/gpui2/src/app/model_context.rs  | 14 +++++++
crates/gpui2/src/executor.rs           |  5 ++
crates/gpui2/src/gpui2.rs              | 28 +++++++++++++++
crates/gpui2/src/window.rs             | 28 +++++++++++++++
crates/settings2/src/settings_file.rs  | 27 +++++++-------
crates/settings2/src/settings_store.rs |  9 +++-
crates/zed2/src/main.rs                |  2 
9 files changed, 161 insertions(+), 42 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -406,24 +406,6 @@ impl AppContext {
             .unwrap()
     }
 
-    pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
-    where
-        G: 'static + Send + Sync,
-    {
-        let mut global = self
-            .global_stacks_by_type
-            .get_mut(&TypeId::of::<G>())
-            .and_then(|stack| stack.pop())
-            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
-            .unwrap();
-        let result = f(global.downcast_mut().unwrap(), self);
-        self.global_stacks_by_type
-            .get_mut(&TypeId::of::<G>())
-            .unwrap()
-            .push(global);
-        result
-    }
-
     pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
         let stack = self
             .global_stacks_by_type
@@ -448,18 +430,20 @@ impl AppContext {
         }
     }
 
-    pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, state: T) {
+    pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, global: T) {
         self.global_stacks_by_type
             .entry(TypeId::of::<T>())
             .or_default()
-            .push(Box::new(state));
+            .push(Box::new(global));
     }
 
-    pub(crate) fn pop_global<T: 'static>(&mut self) {
+    pub(crate) fn pop_global<T: 'static>(&mut self) -> Box<T> {
         self.global_stacks_by_type
             .get_mut(&TypeId::of::<T>())
             .and_then(|stack| stack.pop())
-            .expect("state stack underflow");
+            .expect("state stack underflow")
+            .downcast()
+            .unwrap()
     }
 
     pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
@@ -497,6 +481,10 @@ impl Context for AppContext {
     type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
     type Result<T> = T;
 
+    fn refresh(&mut self) {
+        self.push_effect(Effect::Refresh);
+    }
+
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
@@ -524,6 +512,24 @@ impl Context for AppContext {
     fn read_global<G: 'static + Send + Sync, R>(&self, read: impl FnOnce(&G, &Self) -> R) -> R {
         read(self.global(), self)
     }
+
+    fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
+    where
+        G: 'static + Send + Sync,
+    {
+        let mut global = self
+            .global_stacks_by_type
+            .get_mut(&TypeId::of::<G>())
+            .and_then(|stack| stack.pop())
+            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
+            .unwrap();
+        let result = f(global.downcast_mut().unwrap(), self);
+        self.global_stacks_by_type
+            .get_mut(&TypeId::of::<G>())
+            .unwrap()
+            .push(global);
+        result
+    }
 }
 
 impl MainThread<AppContext> {

crates/gpui2/src/app/async_context.rs 🔗

@@ -13,6 +13,16 @@ impl Context for AsyncAppContext {
     type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
     type Result<T> = Result<T>;
 
+    fn refresh(&mut self) -> Self::Result<()> {
+        let app = self
+            .0
+            .upgrade()
+            .ok_or_else(|| anyhow!("app was released"))?;
+        let mut lock = app.lock(); // Need this to compile
+        lock.refresh();
+        Ok(())
+    }
+
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
@@ -21,7 +31,7 @@ impl Context for AsyncAppContext {
             .0
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.lock();
+        let mut lock = app.lock(); // Need this to compile
         Ok(lock.entity(build_entity))
     }
 
@@ -34,7 +44,7 @@ impl Context for AsyncAppContext {
             .0
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.lock();
+        let mut lock = app.lock(); // Need this to compile
         Ok(lock.update_entity(handle, update))
     }
 
@@ -46,9 +56,21 @@ impl Context for AsyncAppContext {
             .0
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.lock();
+        let lock = app.lock(); // Need this to compile
         Ok(lock.read_global(read))
     }
+
+    fn update_global<G: 'static + Send + Sync, R>(
+        &mut self,
+        update: impl FnOnce(&mut G, &mut Self::BorrowedContext<'_, '_>) -> R,
+    ) -> Self::Result<R> {
+        let app = self
+            .0
+            .upgrade()
+            .ok_or_else(|| anyhow!("app was released"))?;
+        let mut lock = app.lock(); // Need this to compile
+        Ok(lock.update_global(update))
+    }
 }
 
 impl AsyncAppContext {
@@ -106,6 +128,10 @@ impl Context for AsyncWindowContext {
     type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
     type Result<T> = Result<T>;
 
+    fn refresh(&mut self) -> Self::Result<()> {
+        self.app.refresh()
+    }
+
     fn entity<R: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, R>) -> R,
@@ -129,4 +155,12 @@ impl Context for AsyncWindowContext {
     ) -> Result<R> {
         self.app.read_window(self.window, |cx| cx.read_global(read))
     }
+
+    fn update_global<G: 'static + Send + Sync, R>(
+        &mut self,
+        update: impl FnOnce(&mut G, &mut Self::BorrowedContext<'_, '_>) -> R,
+    ) -> Result<R> {
+        self.app
+            .update_window(self.window, |cx| cx.update_global(update))
+    }
 }

crates/gpui2/src/app/model_context.rs 🔗

@@ -136,6 +136,10 @@ impl<'a, T: 'static> Context for ModelContext<'a, T> {
     type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
     type Result<U> = U;
 
+    fn refresh(&mut self) {
+        self.app.refresh();
+    }
+
     fn entity<U: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
@@ -157,4 +161,14 @@ impl<'a, T: 'static> Context for ModelContext<'a, T> {
     ) -> R {
         read(self.app.global(), self)
     }
+
+    fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
+    where
+        G: 'static + Send + Sync,
+    {
+        let mut global = self.app.pop_global::<G>();
+        let result = f(global.as_mut(), self);
+        self.app.push_global(global);
+        result
+    }
 }

crates/gpui2/src/executor.rs 🔗

@@ -128,6 +128,11 @@ impl Executor {
         Task::Spawned(task)
     }
 
+    pub fn block<R>(&self, future: impl Future<Output = R>) -> R {
+        // todo!("integrate with deterministic dispatcher")
+        futures::executor::block_on(future)
+    }
+
     pub fn is_main_thread(&self) -> bool {
         self.dispatcher.is_main_thread()
     }

crates/gpui2/src/gpui2.rs 🔗

@@ -70,6 +70,8 @@ pub trait Context {
     type EntityContext<'a, 'w, T: 'static + Send + Sync>;
     type Result<T>;
 
+    fn refresh(&mut self) -> Self::Result<()>;
+
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
@@ -85,6 +87,13 @@ pub trait Context {
         &self,
         read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
     ) -> Self::Result<R>;
+
+    fn update_global<G, R>(
+        &mut self,
+        f: impl FnOnce(&mut G, &mut Self::BorrowedContext<'_, '_>) -> R,
+    ) -> Self::Result<R>
+    where
+        G: 'static + Send + Sync;
 }
 
 pub enum GlobalKey {
@@ -115,6 +124,10 @@ impl<C: Context> Context for MainThread<C> {
     type EntityContext<'a, 'w, T: 'static + Send + Sync> = MainThread<C::EntityContext<'a, 'w, T>>;
     type Result<T> = C::Result<T>;
 
+    fn refresh(&mut self) -> Self::Result<()> {
+        self.0.refresh()
+    }
+
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
@@ -160,6 +173,21 @@ impl<C: Context> Context for MainThread<C> {
             read(global, cx)
         })
     }
+
+    fn update_global<G: 'static + Send + Sync, R>(
+        &mut self,
+        update: impl FnOnce(&mut G, &mut Self::BorrowedContext<'_, '_>) -> R,
+    ) -> Self::Result<R> {
+        self.0.update_global(|global, cx| {
+            let cx = unsafe {
+                mem::transmute::<
+                    &mut C::BorrowedContext<'_, '_>,
+                    &mut MainThread<C::BorrowedContext<'_, '_>>,
+                >(cx)
+            };
+            update(global, cx)
+        })
+    }
 }
 
 pub trait BorrowAppContext {

crates/gpui2/src/window.rs 🔗

@@ -1060,6 +1060,10 @@ impl Context for WindowContext<'_, '_> {
     type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
     type Result<T> = T;
 
+    fn refresh(&mut self) {
+        self.app.refresh();
+    }
+
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
@@ -1090,6 +1094,16 @@ impl Context for WindowContext<'_, '_> {
     fn read_global<G: 'static + Send + Sync, R>(&self, read: impl FnOnce(&G, &Self) -> R) -> R {
         read(self.app.global(), self)
     }
+
+    fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
+    where
+        G: 'static + Send + Sync,
+    {
+        let mut global = self.app.pop_global::<G>();
+        let result = f(global.as_mut(), self);
+        self.app.push_global(global);
+        result
+    }
 }
 
 impl<'a, 'w> std::ops::Deref for WindowContext<'a, 'w> {
@@ -1540,6 +1554,10 @@ where
     type EntityContext<'b, 'c, U: 'static + Send + Sync> = ViewContext<'b, 'c, U>;
     type Result<U> = U;
 
+    fn refresh(&mut self) {
+        self.app.refresh();
+    }
+
     fn entity<T2: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2,
@@ -1561,6 +1579,16 @@ where
     ) -> R {
         read(self.global(), self)
     }
+
+    fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
+    where
+        G: 'static + Send + Sync,
+    {
+        let mut global = self.app.pop_global::<G>();
+        let result = f(global.as_mut(), self);
+        self.app.push_global(global);
+        result
+    }
 }
 
 impl<'a, 'w, S: 'static> std::ops::Deref for ViewContext<'a, 'w, S> {

crates/settings2/src/settings_file.rs 🔗

@@ -2,7 +2,7 @@ use crate::{settings_store::SettingsStore, Setting};
 use anyhow::Result;
 use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
-use gpui2::{AppContext, Context};
+use gpui2::{AppContext, Context, Executor};
 use std::{
     io::ErrorKind,
     path::{Path, PathBuf},
@@ -48,7 +48,7 @@ pub fn test_settings() -> String {
 }
 
 pub fn watch_config_file(
-    executor: Arc<Background>,
+    executor: &Executor,
     fs: Arc<dyn Fs>,
     path: PathBuf,
 ) -> mpsc::UnboundedReceiver<String> {
@@ -83,7 +83,7 @@ pub fn handle_settings_file_changes(
     mut user_settings_file_rx: mpsc::UnboundedReceiver<String>,
     cx: &mut AppContext,
 ) {
-    let user_settings_content = cx.background().block(user_settings_file_rx.next()).unwrap();
+    let user_settings_content = cx.executor().block(user_settings_file_rx.next()).unwrap();
     cx.update_global(|store: &mut SettingsStore, cx| {
         store
             .set_user_settings(&user_settings_content, cx)
@@ -91,14 +91,15 @@ pub fn handle_settings_file_changes(
     });
     cx.spawn(move |mut cx| async move {
         while let Some(user_settings_content) = user_settings_file_rx.next().await {
-            cx.update(|cx| {
-                cx.update_global(|store: &mut SettingsStore, cx| {
-                    store
-                        .set_user_settings(&user_settings_content, cx)
-                        .log_err();
-                });
-                cx.refresh_windows();
+            let result = cx.update_global(|store: &mut SettingsStore, cx| {
+                store
+                    .set_user_settings(&user_settings_content, cx)
+                    .log_err();
+                cx.refresh();
             });
+            if result.is_err() {
+                break; // App dropped
+            }
         }
     })
     .detach();
@@ -124,10 +125,10 @@ pub fn update_settings_file<T: Setting>(
     update: impl 'static + Send + FnOnce(&mut T::FileContent),
 ) {
     cx.spawn(|cx| async move {
-        let old_text = load_settings(&fs).await;
-        let new_text = cx.read_global(|store: &SettingsStore, cx| {
+        let old_text = load_settings(&fs).await?;
+        let new_text = cx.read_global(|store: &SettingsStore, _cx| {
             store.new_text_for_update::<T>(old_text, update)
-        });
+        })?;
         fs.atomic_write(paths::SETTINGS.clone(), new_text).await?;
         anyhow::Ok(())
     })

crates/settings2/src/settings_store.rs 🔗

@@ -18,7 +18,7 @@ use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _};
 /// A value that can be defined as a user setting.
 ///
 /// Settings can be loaded from a combination of multiple JSON files.
-pub trait Setting: 'static {
+pub trait Setting: 'static + Send + Sync {
     /// The name of a key within the JSON file from which this setting should
     /// be deserialized. If this is `None`, then the setting will be deserialized
     /// from the root object.
@@ -89,7 +89,10 @@ pub struct SettingsStore {
     raw_default_settings: serde_json::Value,
     raw_user_settings: serde_json::Value,
     raw_local_settings: BTreeMap<(usize, Arc<Path>), serde_json::Value>,
-    tab_size_callback: Option<(TypeId, Box<dyn Fn(&dyn Any) -> Option<usize>>)>,
+    tab_size_callback: Option<(
+        TypeId,
+        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,
+    )>,
 }
 
 impl Default for SettingsStore {
@@ -110,7 +113,7 @@ struct SettingValue<T> {
     local_values: Vec<(usize, Arc<Path>, T)>,
 }
 
-trait AnySettingValue {
+trait AnySettingValue: 'static + Send + Sync {
     fn key(&self) -> Option<&'static str>;
     fn setting_type_name(&self) -> &'static str;
     fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting>;

crates/zed2/src/main.rs 🔗

@@ -14,7 +14,7 @@ use log::LevelFilter;
 
 use parking_lot::Mutex;
 use serde::{Deserialize, Serialize};
-use settings::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore};
+use settings2::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore};
 use simplelog::ConfigBuilder;
 use smol::process::Command;
 use std::{