Merge pull request #265 from zed-industries/fix-collaborators-test

Nathan Sobo created

Fix flaky contacts test

Change summary

Cargo.lock                      |  10 
crates/buffer/Cargo.toml        |   6 
crates/buffer/src/lib.rs        |  26 --
crates/chat_panel/src/lib.rs    |   2 
crates/client/src/lib.rs        |   4 
crates/collections/Cargo.toml   |  10 
crates/collections/src/lib.rs   |  26 ++
crates/editor/src/lib.rs        |   2 
crates/file_finder/src/lib.rs   |   2 
crates/gpui/src/app.rs          | 366 +++++++++++++++-------------------
crates/gpui/src/views/select.rs |   2 
crates/project_panel/src/lib.rs |   2 
crates/server/Cargo.toml        |   2 
crates/server/src/rpc.rs        |  18 
crates/server/src/rpc/store.rs  |   9 
15 files changed, 239 insertions(+), 248 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -746,10 +746,10 @@ dependencies = [
  "anyhow",
  "arrayvec 0.7.1",
  "clock",
+ "collections",
  "gpui",
  "log",
  "rand 0.8.3",
- "seahash",
  "smallvec",
  "sum_tree",
 ]
@@ -1057,6 +1057,13 @@ dependencies = [
  "objc",
 ]
 
+[[package]]
+name = "collections"
+version = "0.1.0"
+dependencies = [
+ "seahash",
+]
+
 [[package]]
 name = "color_quant"
 version = "1.1.0"
@@ -5739,6 +5746,7 @@ dependencies = [
  "async-tungstenite",
  "base64 0.13.0",
  "clap 3.0.0-beta.2",
+ "collections",
  "comrak",
  "either",
  "envy",

crates/buffer/Cargo.toml 🔗

@@ -4,19 +4,19 @@ version = "0.1.0"
 edition = "2021"
 
 [features]
-test-support = ["rand", "seahash"]
+test-support = ["rand"]
 
 [dependencies]
 clock = { path = "../clock" }
+collections = { path = "../collections" }
 sum_tree = { path = "../sum_tree" }
 anyhow = "1.0.38"
 arrayvec = "0.7.1"
 log = "0.4"
 rand = { version = "0.8.3", optional = true }
-seahash = { version = "4.1", optional = true }
 smallvec = { version = "1.6", features = ["union"] }
 
 [dev-dependencies]
+collections = { path = "../collections", features = ["test-support"] }
 gpui = { path = "../gpui", features = ["test-support"] }
-seahash = "4.1"
 rand = "0.8.3"

crates/buffer/src/lib.rs 🔗

@@ -12,6 +12,7 @@ mod tests;
 pub use anchor::*;
 use anyhow::{anyhow, Result};
 use clock::ReplicaId;
+use collections::{HashMap, HashSet};
 use operation_queue::OperationQueue;
 pub use point::*;
 pub use point_utf16::*;
@@ -31,31 +32,6 @@ use std::{
 pub use sum_tree::Bias;
 use sum_tree::{FilterCursor, SumTree};
 
-#[cfg(any(test, feature = "test-support"))]
-#[derive(Clone, Default)]
-pub struct DeterministicState;
-
-#[cfg(any(test, feature = "test-support"))]
-impl std::hash::BuildHasher for DeterministicState {
-    type Hasher = seahash::SeaHasher;
-
-    fn build_hasher(&self) -> Self::Hasher {
-        seahash::SeaHasher::new()
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-type HashMap<K, V> = std::collections::HashMap<K, V, DeterministicState>;
-
-#[cfg(any(test, feature = "test-support"))]
-type HashSet<T> = std::collections::HashSet<T, DeterministicState>;
-
-#[cfg(not(any(test, feature = "test-support")))]
-type HashMap<K, V> = std::collections::HashMap<K, V>;
-
-#[cfg(not(any(test, feature = "test-support")))]
-type HashSet<T> = std::collections::HashSet<T>;
-
 #[derive(Clone)]
 pub struct Buffer {
     fragments: SumTree<Fragment>,

crates/chat_panel/src/lib.rs 🔗

@@ -96,7 +96,7 @@ impl ChatPanel {
         });
 
         let mut message_list = ListState::new(0, Orientation::Bottom, 1000., {
-            let this = cx.handle().downgrade();
+            let this = cx.weak_handle();
             move |ix, cx| {
                 let this = this.upgrade(cx).unwrap().read(cx);
                 let message = this.active_channel.as_ref().unwrap().0.read(cx).message(ix);

crates/client/src/lib.rs 🔗

@@ -272,7 +272,7 @@ impl Client {
         let subscription_id = (TypeId::of::<T>(), Default::default());
         let client = self.clone();
         let mut state = self.state.write();
-        let model = cx.handle().downgrade();
+        let model = cx.weak_handle();
         let prev_extractor = state
             .entity_id_extractors
             .insert(subscription_id.0, Box::new(|_| Default::default()));
@@ -317,7 +317,7 @@ impl Client {
         let subscription_id = (TypeId::of::<T>(), remote_id);
         let client = self.clone();
         let mut state = self.state.write();
-        let model = cx.handle().downgrade();
+        let model = cx.weak_handle();
         state
             .entity_id_extractors
             .entry(subscription_id.0)

crates/collections/Cargo.toml 🔗

@@ -0,0 +1,10 @@
+[package]
+name = "collections"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+test-support = ["seahash"]
+
+[dependencies]
+seahash = { version = "4.1", optional = true }

crates/collections/src/lib.rs 🔗

@@ -0,0 +1,26 @@
+#[cfg(feature = "test-support")]
+#[derive(Clone, Default)]
+pub struct DeterministicState;
+
+#[cfg(feature = "test-support")]
+impl std::hash::BuildHasher for DeterministicState {
+    type Hasher = seahash::SeaHasher;
+
+    fn build_hasher(&self) -> Self::Hasher {
+        seahash::SeaHasher::new()
+    }
+}
+
+#[cfg(feature = "test-support")]
+pub type HashMap<K, V> = std::collections::HashMap<K, V, DeterministicState>;
+
+#[cfg(feature = "test-support")]
+pub type HashSet<T> = std::collections::HashSet<T, DeterministicState>;
+
+#[cfg(not(feature = "test-support"))]
+pub type HashMap<K, V> = std::collections::HashMap<K, V>;
+
+#[cfg(not(feature = "test-support"))]
+pub type HashSet<T> = std::collections::HashSet<T>;
+
+pub use std::collections::*;

crates/editor/src/lib.rs 🔗

@@ -487,7 +487,7 @@ impl Editor {
             )
         });
         Self {
-            handle: cx.handle().downgrade(),
+            handle: cx.weak_handle(),
             buffer,
             display_map,
             selection_set_id,

crates/file_finder/src/lib.rs 🔗

@@ -286,7 +286,7 @@ impl FileFinder {
             .detach();
 
         Self {
-            handle: cx.handle().downgrade(),
+            handle: cx.weak_handle(),
             settings,
             project,
             query_editor,

crates/gpui/src/app.rs 🔗

@@ -342,10 +342,8 @@ impl App {
 
     fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T {
         let mut state = self.0.borrow_mut();
-        state.pending_flushes += 1;
-        let result = callback(&mut *state);
+        let result = state.update(callback);
         state.pending_notifications.clear();
-        state.flush_effects();
         result
     }
 }
@@ -406,11 +404,7 @@ impl TestAppContext {
         T: Entity,
         F: FnOnce(&mut ModelContext<T>) -> T,
     {
-        let mut state = self.cx.borrow_mut();
-        state.pending_flushes += 1;
-        let handle = state.add_model(build_model);
-        state.flush_effects();
-        handle
+        self.cx.borrow_mut().add_model(build_model)
     }
 
     pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
@@ -436,11 +430,7 @@ impl TestAppContext {
         T: View,
         F: FnOnce(&mut ViewContext<T>) -> T,
     {
-        let mut state = self.cx.borrow_mut();
-        state.pending_flushes += 1;
-        let handle = state.add_view(window_id, build_view);
-        state.flush_effects();
-        handle
+        self.cx.borrow_mut().add_view(window_id, build_view)
     }
 
     pub fn add_option_view<T, F>(
@@ -452,11 +442,7 @@ impl TestAppContext {
         T: View,
         F: FnOnce(&mut ViewContext<T>) -> Option<T>,
     {
-        let mut state = self.cx.borrow_mut();
-        state.pending_flushes += 1;
-        let handle = state.add_option_view(window_id, build_view);
-        state.flush_effects();
-        handle
+        self.cx.borrow_mut().add_option_view(window_id, build_view)
     }
 
     pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
@@ -535,11 +521,7 @@ impl AsyncAppContext {
     }
 
     pub fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T {
-        let mut state = self.0.borrow_mut();
-        state.pending_flushes += 1;
-        let result = callback(&mut *state);
-        state.flush_effects();
-        result
+        self.0.borrow_mut().update(callback)
     }
 
     pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
@@ -569,11 +551,7 @@ impl UpdateModel for AsyncAppContext {
         handle: &ModelHandle<E>,
         update: &mut dyn FnMut(&mut E, &mut ModelContext<E>) -> O,
     ) -> O {
-        let mut state = self.0.borrow_mut();
-        state.pending_flushes += 1;
-        let result = state.update_model(handle, update);
-        state.flush_effects();
-        result
+        self.0.borrow_mut().update_model(handle, update)
     }
 }
 
@@ -607,11 +585,7 @@ impl UpdateView for AsyncAppContext {
     where
         T: View,
     {
-        let mut state = self.0.borrow_mut();
-        state.pending_flushes += 1;
-        let result = state.update_view(handle, update);
-        state.flush_effects();
-        result
+        self.0.borrow_mut().update_view(handle, update)
     }
 }
 
@@ -636,11 +610,7 @@ impl UpdateModel for TestAppContext {
         handle: &ModelHandle<T>,
         update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
     ) -> O {
-        let mut state = self.cx.borrow_mut();
-        state.pending_flushes += 1;
-        let result = state.update_model(handle, update);
-        state.flush_effects();
-        result
+        self.cx.borrow_mut().update_model(handle, update)
     }
 }
 
@@ -665,11 +635,7 @@ impl UpdateView for TestAppContext {
     where
         T: View,
     {
-        let mut state = self.cx.borrow_mut();
-        state.pending_flushes += 1;
-        let result = state.update_view(handle, update);
-        state.flush_effects();
-        result
+        self.cx.borrow_mut().update_view(handle, update)
     }
 }
 
@@ -727,6 +693,7 @@ impl MutableAppContext {
         foreground_platform: Rc<dyn platform::ForegroundPlatform>,
         font_cache: Arc<FontCache>,
         asset_source: impl AssetSource,
+        // entity_drop_tx:
     ) -> Self {
         Self {
             weak_self: None,
@@ -941,9 +908,9 @@ impl MutableAppContext {
             .collect()
     }
 
-    pub fn update<T, F: FnOnce() -> T>(&mut self, callback: F) -> T {
+    pub fn update<T, F: FnOnce(&mut Self) -> T>(&mut self, callback: F) -> T {
         self.pending_flushes += 1;
-        let result = callback();
+        let result = callback(self);
         self.flush_effects();
         result
     }
@@ -1124,46 +1091,44 @@ impl MutableAppContext {
         path: &[usize],
         action: &dyn AnyAction,
     ) -> bool {
-        self.pending_flushes += 1;
-        let mut halted_dispatch = false;
-
-        for view_id in path.iter().rev() {
-            if let Some(mut view) = self.cx.views.remove(&(window_id, *view_id)) {
-                let type_id = view.as_any().type_id();
-
-                if let Some((name, mut handlers)) = self
-                    .actions
-                    .get_mut(&type_id)
-                    .and_then(|h| h.remove_entry(&action.id()))
-                {
-                    for handler in handlers.iter_mut().rev() {
-                        let halt_dispatch =
-                            handler(view.as_mut(), action, self, window_id, *view_id);
-                        if halt_dispatch {
-                            halted_dispatch = true;
-                            break;
+        self.update(|this| {
+            let mut halted_dispatch = false;
+            for view_id in path.iter().rev() {
+                if let Some(mut view) = this.cx.views.remove(&(window_id, *view_id)) {
+                    let type_id = view.as_any().type_id();
+
+                    if let Some((name, mut handlers)) = this
+                        .actions
+                        .get_mut(&type_id)
+                        .and_then(|h| h.remove_entry(&action.id()))
+                    {
+                        for handler in handlers.iter_mut().rev() {
+                            let halt_dispatch =
+                                handler(view.as_mut(), action, this, window_id, *view_id);
+                            if halt_dispatch {
+                                halted_dispatch = true;
+                                break;
+                            }
                         }
+                        this.actions
+                            .get_mut(&type_id)
+                            .unwrap()
+                            .insert(name, handlers);
                     }
-                    self.actions
-                        .get_mut(&type_id)
-                        .unwrap()
-                        .insert(name, handlers);
-                }
 
-                self.cx.views.insert((window_id, *view_id), view);
+                    this.cx.views.insert((window_id, *view_id), view);
 
-                if halted_dispatch {
-                    break;
+                    if halted_dispatch {
+                        break;
+                    }
                 }
             }
-        }
 
-        if !halted_dispatch {
-            self.dispatch_global_action_any(action);
-        }
-
-        self.flush_effects();
-        halted_dispatch
+            if !halted_dispatch {
+                this.dispatch_global_action_any(action);
+            }
+            halted_dispatch
+        })
     }
 
     pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
@@ -1171,14 +1136,14 @@ impl MutableAppContext {
     }
 
     fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) {
-        if let Some((name, mut handlers)) = self.global_actions.remove_entry(&action.id()) {
-            self.pending_flushes += 1;
-            for handler in handlers.iter_mut().rev() {
-                handler(action, self);
+        self.update(|this| {
+            if let Some((name, mut handlers)) = this.global_actions.remove_entry(&action.id()) {
+                for handler in handlers.iter_mut().rev() {
+                    handler(action, this);
+                }
+                this.global_actions.insert(name, handlers);
             }
-            self.global_actions.insert(name, handlers);
-            self.flush_effects();
-        }
+        })
     }
 
     pub fn add_bindings<T: IntoIterator<Item = keymap::Binding>>(&mut self, bindings: T) {
@@ -1230,14 +1195,14 @@ impl MutableAppContext {
         T: Entity,
         F: FnOnce(&mut ModelContext<T>) -> T,
     {
-        self.pending_flushes += 1;
-        let model_id = post_inc(&mut self.next_entity_id);
-        let handle = ModelHandle::new(model_id, &self.cx.ref_counts);
-        let mut cx = ModelContext::new(self, model_id);
-        let model = build_model(&mut cx);
-        self.cx.models.insert(model_id, Box::new(model));
-        self.flush_effects();
-        handle
+        self.update(|this| {
+            let model_id = post_inc(&mut this.next_entity_id);
+            let handle = ModelHandle::new(model_id, &this.cx.ref_counts);
+            let mut cx = ModelContext::new(this, model_id);
+            let model = build_model(&mut cx);
+            this.cx.models.insert(model_id, Box::new(model));
+            handle
+        })
     }
 
     pub fn add_window<T, F>(
@@ -1249,26 +1214,26 @@ impl MutableAppContext {
         T: View,
         F: FnOnce(&mut ViewContext<T>) -> T,
     {
-        self.pending_flushes += 1;
-        let window_id = post_inc(&mut self.next_window_id);
-        let root_view = self.add_view(window_id, build_root_view);
+        self.update(|this| {
+            let window_id = post_inc(&mut this.next_window_id);
+            let root_view = this.add_view(window_id, build_root_view);
 
-        self.cx.windows.insert(
-            window_id,
-            Window {
-                root_view: root_view.clone().into(),
-                focused_view_id: root_view.id(),
-                invalidation: None,
-            },
-        );
-        self.open_platform_window(window_id, window_options);
-        root_view.update(self, |view, cx| {
-            view.on_focus(cx);
-            cx.notify();
-        });
-        self.flush_effects();
+            this.cx.windows.insert(
+                window_id,
+                Window {
+                    root_view: root_view.clone().into(),
+                    focused_view_id: root_view.id(),
+                    invalidation: None,
+                },
+            );
+            this.open_platform_window(window_id, window_options);
+            root_view.update(this, |view, cx| {
+                view.on_focus(cx);
+                cx.notify();
+            });
 
-        (window_id, root_view)
+            (window_id, root_view)
+        })
     }
 
     pub fn remove_window(&mut self, window_id: usize) {
@@ -1377,25 +1342,25 @@ impl MutableAppContext {
         T: View,
         F: FnOnce(&mut ViewContext<T>) -> Option<T>,
     {
-        let view_id = post_inc(&mut self.next_entity_id);
-        self.pending_flushes += 1;
-        let handle = ViewHandle::new(window_id, view_id, &self.cx.ref_counts);
-        let mut cx = ViewContext::new(self, window_id, view_id);
-        let handle = if let Some(view) = build_view(&mut cx) {
-            self.cx.views.insert((window_id, view_id), Box::new(view));
-            if let Some(window) = self.cx.windows.get_mut(&window_id) {
-                window
-                    .invalidation
-                    .get_or_insert_with(Default::default)
-                    .updated
-                    .insert(view_id);
-            }
-            Some(handle)
-        } else {
-            None
-        };
-        self.flush_effects();
-        handle
+        self.update(|this| {
+            let view_id = post_inc(&mut this.next_entity_id);
+            let handle = ViewHandle::new(window_id, view_id, &this.cx.ref_counts);
+            let mut cx = ViewContext::new(this, window_id, view_id);
+            let handle = if let Some(view) = build_view(&mut cx) {
+                this.cx.views.insert((window_id, view_id), Box::new(view));
+                if let Some(window) = this.cx.windows.get_mut(&window_id) {
+                    window
+                        .invalidation
+                        .get_or_insert_with(Default::default)
+                        .updated
+                        .insert(view_id);
+                }
+                Some(handle)
+            } else {
+                None
+            };
+            handle
+        })
     }
 
     pub fn element_state<Tag: 'static, T: 'static + Default>(
@@ -1647,27 +1612,25 @@ impl MutableAppContext {
             return;
         }
 
-        self.pending_flushes += 1;
-
-        let blurred_id = self.cx.windows.get_mut(&window_id).map(|window| {
-            let blurred_id = window.focused_view_id;
-            window.focused_view_id = focused_id;
-            blurred_id
-        });
+        self.update(|this| {
+            let blurred_id = this.cx.windows.get_mut(&window_id).map(|window| {
+                let blurred_id = window.focused_view_id;
+                window.focused_view_id = focused_id;
+                blurred_id
+            });
 
-        if let Some(blurred_id) = blurred_id {
-            if let Some(mut blurred_view) = self.cx.views.remove(&(window_id, blurred_id)) {
-                blurred_view.on_blur(self, window_id, blurred_id);
-                self.cx.views.insert((window_id, blurred_id), blurred_view);
+            if let Some(blurred_id) = blurred_id {
+                if let Some(mut blurred_view) = this.cx.views.remove(&(window_id, blurred_id)) {
+                    blurred_view.on_blur(this, window_id, blurred_id);
+                    this.cx.views.insert((window_id, blurred_id), blurred_view);
+                }
             }
-        }
-
-        if let Some(mut focused_view) = self.cx.views.remove(&(window_id, focused_id)) {
-            focused_view.on_focus(self, window_id, focused_id);
-            self.cx.views.insert((window_id, focused_id), focused_view);
-        }
 
-        self.flush_effects();
+            if let Some(mut focused_view) = this.cx.views.remove(&(window_id, focused_id)) {
+                focused_view.on_focus(this, window_id, focused_id);
+                this.cx.views.insert((window_id, focused_id), focused_view);
+            }
+        })
     }
 
     pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T>
@@ -1713,18 +1676,18 @@ impl UpdateModel for MutableAppContext {
         update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
     ) -> V {
         if let Some(mut model) = self.cx.models.remove(&handle.model_id) {
-            self.pending_flushes += 1;
-            let mut cx = ModelContext::new(self, handle.model_id);
-            let result = update(
-                model
-                    .as_any_mut()
-                    .downcast_mut()
-                    .expect("downcast is type safe"),
-                &mut cx,
-            );
-            self.cx.models.insert(handle.model_id, model);
-            self.flush_effects();
-            result
+            self.update(|this| {
+                let mut cx = ModelContext::new(this, handle.model_id);
+                let result = update(
+                    model
+                        .as_any_mut()
+                        .downcast_mut()
+                        .expect("downcast is type safe"),
+                    &mut cx,
+                );
+                this.cx.models.insert(handle.model_id, model);
+                result
+            })
         } else {
             panic!("circular model update");
         }
@@ -1759,25 +1722,25 @@ impl UpdateView for MutableAppContext {
     where
         T: View,
     {
-        self.pending_flushes += 1;
-        let mut view = self
-            .cx
-            .views
-            .remove(&(handle.window_id, handle.view_id))
-            .expect("circular view update");
-
-        let mut cx = ViewContext::new(self, handle.window_id, handle.view_id);
-        let result = update(
-            view.as_any_mut()
-                .downcast_mut()
-                .expect("downcast is type safe"),
-            &mut cx,
-        );
-        self.cx
-            .views
-            .insert((handle.window_id, handle.view_id), view);
-        self.flush_effects();
-        result
+        self.update(|this| {
+            let mut view = this
+                .cx
+                .views
+                .remove(&(handle.window_id, handle.view_id))
+                .expect("circular view update");
+
+            let mut cx = ViewContext::new(this, handle.window_id, handle.view_id);
+            let result = update(
+                view.as_any_mut()
+                    .downcast_mut()
+                    .expect("downcast is type safe"),
+                &mut cx,
+            );
+            this.cx
+                .views
+                .insert((handle.window_id, handle.view_id), view);
+            result
+        })
     }
 }
 
@@ -2110,7 +2073,7 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         S::Event: 'static,
         F: 'static + FnMut(&mut T, ModelHandle<S>, &S::Event, &mut ModelContext<T>),
     {
-        let subscriber = self.handle().downgrade();
+        let subscriber = self.weak_handle();
         self.app
             .subscribe_internal(handle, move |emitter, event, cx| {
                 if let Some(subscriber) = subscriber.upgrade(cx) {
@@ -2129,7 +2092,7 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         S: Entity,
         F: 'static + FnMut(&mut T, ModelHandle<S>, &mut ModelContext<T>),
     {
-        let observer = self.handle().downgrade();
+        let observer = self.weak_handle();
         self.app.observe_internal(handle, move |observed, cx| {
             if let Some(observer) = observer.upgrade(cx) {
                 observer.update(cx, |observer, cx| {
@@ -2146,6 +2109,10 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         ModelHandle::new(self.model_id, &self.app.cx.ref_counts)
     }
 
+    pub fn weak_handle(&self) -> WeakModelHandle<T> {
+        WeakModelHandle::new(self.model_id)
+    }
+
     pub fn spawn<F, Fut, S>(&self, f: F) -> Task<S>
     where
         F: FnOnce(ModelHandle<T>, AsyncAppContext) -> Fut,
@@ -2162,7 +2129,7 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
-        let handle = self.handle().downgrade();
+        let handle = self.weak_handle();
         self.app.spawn(|cx| f(handle, cx))
     }
 }
@@ -2241,6 +2208,10 @@ impl<'a, T: View> ViewContext<'a, T> {
         ViewHandle::new(self.window_id, self.view_id, &self.app.cx.ref_counts)
     }
 
+    pub fn weak_handle(&self) -> WeakViewHandle<T> {
+        WeakViewHandle::new(self.window_id, self.view_id)
+    }
+
     pub fn window_id(&self) -> usize {
         self.window_id
     }
@@ -2336,7 +2307,7 @@ impl<'a, T: View> ViewContext<'a, T> {
         H: Handle<E>,
         F: 'static + FnMut(&mut T, H, &E::Event, &mut ViewContext<T>),
     {
-        let subscriber = self.handle().downgrade();
+        let subscriber = self.weak_handle();
         self.app
             .subscribe_internal(handle, move |emitter, event, cx| {
                 if let Some(subscriber) = subscriber.upgrade(cx) {
@@ -2356,7 +2327,7 @@ impl<'a, T: View> ViewContext<'a, T> {
         H: Handle<E>,
         F: 'static + FnMut(&mut T, H, &mut ViewContext<T>),
     {
-        let observer = self.handle().downgrade();
+        let observer = self.weak_handle();
         self.app.observe_internal(handle, move |observed, cx| {
             if let Some(observer) = observer.upgrade(cx) {
                 observer.update(cx, |observer, cx| {
@@ -2400,7 +2371,7 @@ impl<'a, T: View> ViewContext<'a, T> {
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
-        let handle = self.handle().downgrade();
+        let handle = self.weak_handle();
         self.app.spawn(|cx| f(handle, cx))
     }
 }
@@ -3328,7 +3299,9 @@ struct RefCounts {
 impl RefCounts {
     fn inc_model(&mut self, model_id: usize) {
         match self.entity_counts.entry(model_id) {
-            Entry::Occupied(mut entry) => *entry.get_mut() += 1,
+            Entry::Occupied(mut entry) => {
+                *entry.get_mut() += 1;
+            }
             Entry::Vacant(entry) => {
                 entry.insert(1);
                 self.dropped_models.remove(&model_id);
@@ -3395,16 +3368,11 @@ impl RefCounts {
         HashSet<(usize, usize)>,
         HashSet<(TypeId, ElementStateId)>,
     ) {
-        let mut dropped_models = HashSet::new();
-        let mut dropped_views = HashSet::new();
-        let mut dropped_element_states = HashSet::new();
-        std::mem::swap(&mut self.dropped_models, &mut dropped_models);
-        std::mem::swap(&mut self.dropped_views, &mut dropped_views);
-        std::mem::swap(
-            &mut self.dropped_element_states,
-            &mut dropped_element_states,
-        );
-        (dropped_models, dropped_views, dropped_element_states)
+        (
+            std::mem::take(&mut self.dropped_models),
+            std::mem::take(&mut self.dropped_views),
+            std::mem::take(&mut self.dropped_element_states),
+        )
     }
 }
 
@@ -3711,7 +3679,7 @@ mod tests {
         assert!(!*model_released.lock());
         assert!(!*view_released.lock());
 
-        cx.update(move || {
+        cx.update(move |_| {
             drop(model);
         });
         assert!(*model_released.lock());
@@ -3817,7 +3785,7 @@ mod tests {
             cx.subscribe(&observed_model, |_, _, _, _| {}).detach();
         });
 
-        cx.update(|| {
+        cx.update(|_| {
             drop(observing_view);
             drop(observing_model);
         });
@@ -3909,7 +3877,7 @@ mod tests {
             cx.observe(&observed_model, |_, _, _| {}).detach();
         });
 
-        cx.update(|| {
+        cx.update(|_| {
             drop(observing_view);
             drop(observing_model);
         });

crates/gpui/src/views/select.rs 🔗

@@ -42,7 +42,7 @@ impl Select {
         render_item: F,
     ) -> Self {
         Self {
-            handle: cx.handle().downgrade(),
+            handle: cx.weak_handle(),
             render_item: Box::new(render_item),
             selected_item_ix: 0,
             item_count,

crates/project_panel/src/lib.rs 🔗

@@ -108,7 +108,7 @@ impl ProjectPanel {
                 visible_entries: Default::default(),
                 expanded_dir_ids: Default::default(),
                 selection: None,
-                handle: cx.handle().downgrade(),
+                handle: cx.weak_handle(),
             };
             this.update_visible_entries(None, cx);
             this

crates/server/Cargo.toml 🔗

@@ -13,6 +13,7 @@ name = "seed"
 required-features = ["seed-support"]
 
 [dependencies]
+collections = { path = "../collections" }
 rpc = { path = "../rpc" }
 
 anyhow = "1.0.40"
@@ -54,6 +55,7 @@ version = "0.5.2"
 features = ["runtime-async-std-rustls", "postgres", "time", "uuid"]
 
 [dev-dependencies]
+collections = { path = "../collections", features = ["test-support"] }
 gpui = { path = "../gpui" }
 zed = { path = "../zed", features = ["test-support"] }
 

crates/server/src/rpc.rs 🔗

@@ -8,6 +8,7 @@ use super::{
 use anyhow::anyhow;
 use async_std::task;
 use async_tungstenite::{tungstenite::protocol::Role, WebSocketStream};
+use collections::{HashMap, HashSet};
 use futures::{future::BoxFuture, FutureExt};
 use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
 use postage::{mpsc, prelude::Sink as _, prelude::Stream as _};
@@ -16,14 +17,7 @@ use rpc::{
     Connection, ConnectionId, Peer, TypedEnvelope,
 };
 use sha1::{Digest as _, Sha1};
-use std::{
-    any::TypeId,
-    collections::{HashMap, HashSet},
-    future::Future,
-    mem,
-    sync::Arc,
-    time::Instant,
-};
+use std::{any::TypeId, future::Future, mem, sync::Arc, time::Instant};
 use store::{Store, Worktree};
 use surf::StatusCode;
 use tide::log;
@@ -220,7 +214,7 @@ impl Server {
         let receipt = request.receipt();
         let host_user_id = self.state().user_id_for_connection(request.sender_id)?;
 
-        let mut contact_user_ids = HashSet::new();
+        let mut contact_user_ids = HashSet::default();
         contact_user_ids.insert(host_user_id);
         for github_login in request.payload.authorized_logins {
             match self.app_state.db.create_user(&github_login, false).await {
@@ -2238,6 +2232,12 @@ mod tests {
             })
             .await;
 
+        worktree_a
+            .condition(&cx_a, |worktree, cx| {
+                worktree.collaborators().contains_key(&client_b.peer_id)
+            })
+            .await;
+
         cx_a.update(move |_| drop(worktree_a));
         client_a
             .user_store

crates/server/src/rpc/store.rs 🔗

@@ -1,7 +1,8 @@
 use crate::db::{ChannelId, UserId};
 use anyhow::anyhow;
+use collections::{HashMap, HashSet};
 use rpc::{proto, ConnectionId};
-use std::collections::{hash_map, HashMap, HashSet};
+use std::collections::hash_map;
 
 #[derive(Default)]
 pub struct Store {
@@ -172,15 +173,15 @@ impl Store {
     }
 
     pub fn contacts_for_user(&self, user_id: UserId) -> Vec<proto::Contact> {
-        let mut contacts = HashMap::new();
+        let mut contacts = HashMap::default();
         for worktree_id in self
             .visible_worktrees_by_user_id
             .get(&user_id)
-            .unwrap_or(&HashSet::new())
+            .unwrap_or(&HashSet::default())
         {
             let worktree = &self.worktrees[worktree_id];
 
-            let mut guests = HashSet::new();
+            let mut guests = HashSet::default();
             if let Ok(share) = worktree.share() {
                 for guest_connection_id in share.guests.keys() {
                     if let Ok(user_id) = self.user_id_for_connection(*guest_connection_id) {