WIP

Nathan Sobo created

Change summary

crates/buffer_diff/src/buffer_diff.rs   |  9 ++-
crates/channel/src/channel_store.rs     |  3 
crates/gpui/src/app/entity_map.rs       |  9 ++-
crates/gpui/src/app/test_context.rs     |  3 
crates/gpui/src/window.rs               | 19 +++++--
crates/language/src/buffer.rs           | 11 ++-
crates/language_model/src/registry.rs   |  4 
crates/multi_buffer/src/multi_buffer.rs | 64 ++++++++++++++------------
crates/project/src/buffer_store.rs      |  6 ++
crates/project/src/project.rs           |  3 
crates/project/src/project_tests.rs     | 34 +++++++-------
crates/project/src/task_inventory.rs    |  2 
crates/web_search/src/web_search.rs     |  4 
crates/worktree/src/worktree.rs         | 36 ++++++++------
14 files changed, 118 insertions(+), 89 deletions(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -4,6 +4,7 @@ use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task,
 use language::{Language, LanguageRegistry};
 use rope::Rope;
 use std::{
+    cell::Ref,
     cmp::Ordering,
     future::Future,
     iter,
@@ -1109,9 +1110,11 @@ impl BufferDiff {
         let unstaged_counterpart = self
             .secondary_diff
             .as_ref()
-            .map(|diff| &diff.read(cx).inner);
-        self.inner
-            .hunks_intersecting_range(range, buffer_snapshot, unstaged_counterpart)
+            .map(|diff| Ref::map(diff.read(cx), |d| &d.inner));
+        // self.inner
+        //     .hunks_intersecting_range(range, buffer_snapshot, unstaged_counterpart)
+        // todo! Figure out what to do here
+        None.into_iter()
     }
 
     pub fn hunks_intersecting_range_rev<'a>(

crates/channel/src/channel_store.rs 🔗

@@ -976,7 +976,8 @@ impl ChannelStore {
             if let OpenEntityHandle::Open(buffer) = buffer {
                 if let Some(buffer) = buffer.upgrade() {
                     let channel_buffer = buffer.read(cx);
-                    let buffer = channel_buffer.buffer().read(cx);
+                    let buffer = channel_buffer.buffer();
+                    let buffer = buffer.read(cx);
                     buffer_versions.push(proto::ChannelBufferVersion {
                         channel_id: channel_buffer.channel_id.0,
                         epoch: channel_buffer.epoch(),

crates/gpui/src/app/entity_map.rs 🔗

@@ -6,7 +6,7 @@ use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 use slotmap::{KeyData, SecondaryMap, SlotMap};
 use std::{
     any::{Any, TypeId, type_name},
-    cell::RefCell,
+    cell::{Ref, RefCell},
     cmp::Ordering,
     fmt::{self, Display},
     hash::{Hash, Hasher},
@@ -371,8 +371,11 @@ impl<T: 'static> Entity<T> {
     }
 
     /// Grab a reference to this entity from the context.
-    pub fn read<'a>(&self, cx: &'a App) -> &'a T {
-        cx.entities.read(self)
+    /// todo! remove the cx param
+    pub fn read(&self, _cx: &App) -> Ref<T> {
+        Ref::map(self.any_entity.entity_data.borrow(), |data| {
+            data.downcast_ref().unwrap()
+        })
     }
 
     /// Read the entity referenced by this handle with the given function.

crates/gpui/src/app/test_context.rs 🔗

@@ -624,7 +624,8 @@ impl<V> Entity<V> {
                             handle
                                 .upgrade()
                                 .expect("view dropped with pending condition")
-                                .read(cx),
+                                .read(cx)
+                                .deref(),
                             cx,
                         ) {
                             break;

crates/gpui/src/window.rs 🔗

@@ -31,6 +31,8 @@ use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle};
 use refineable::Refineable;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
+use std::cell::Ref;
+use std::ops::Deref;
 use std::{
     any::{Any, TypeId},
     borrow::Cow,
@@ -4139,7 +4141,8 @@ impl Window {
         if let Some(inspector_id) = _inspector_id {
             if let Some(inspector) = &self.inspector {
                 let inspector = inspector.clone();
-                let active_element_id = inspector.read(cx).active_element_id();
+                let inspector_ref = inspector.read(cx);
+                let active_element_id = inspector_ref.active_element_id();
                 if Some(inspector_id) == active_element_id {
                     return inspector.update(cx, |inspector, _cx| {
                         inspector.with_active_element_state(self, f)
@@ -4213,9 +4216,9 @@ impl Window {
 
     #[cfg(any(feature = "inspector", debug_assertions))]
     fn paint_inspector_hitbox(&mut self, cx: &App) {
-        if let Some(inspector) = self.inspector.as_ref() {
-            let inspector = inspector.read(cx);
-            if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame)
+        if let Some(inspector) = self.inspector.clone() {
+            if let Some((hitbox_id, _)) =
+                self.hovered_inspector_hitbox(inspector.read(cx).deref(), &self.next_frame)
             {
                 if let Some(hitbox) = self
                     .next_frame
@@ -4379,7 +4382,7 @@ impl<V: 'static + Render> WindowHandle<V> {
     /// Read the root view out of this window.
     ///
     /// This will fail if the window is closed or if the root view's type does not match `V`.
-    pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
+    pub fn read(&self, cx: &App) -> Result<Ref<V>> {
         let x = cx
             .windows
             .get(self.id)
@@ -4392,7 +4395,7 @@ impl<V: 'static + Render> WindowHandle<V> {
             .context("window not found")?
             .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
 
-        Ok(x.read(cx))
+        todo!()
     }
 
     /// Read the root view out of this window, with a callback
@@ -4402,7 +4405,9 @@ impl<V: 'static + Render> WindowHandle<V> {
     where
         C: AppContext,
     {
-        cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
+        cx.read_window(self, |root_view, cx| {
+            read_with(root_view.read(cx).deref(), cx)
+        })
     }
 
     /// Read the root view pointer off of this window.

crates/language/src/buffer.rs 🔗

@@ -41,7 +41,7 @@ use std::{
     cell::Cell,
     cmp::{self, Ordering, Reverse},
     collections::{BTreeMap, BTreeSet},
-    ffi::OsStr,
+    ffi::{OsStr, OsString},
     future::Future,
     iter::{self, Iterator, Peekable},
     mem,
@@ -343,7 +343,7 @@ pub trait File: Send + Sync + Any {
 
     /// Returns the last component of this handle's absolute path. If this handle refers to the root
     /// of its worktree, then this method will return the name of the worktree itself.
-    fn file_name<'a>(&'a self, cx: &'a App) -> &'a OsStr;
+    fn file_name<'a>(&'a self, cx: &'a App) -> OsString;
 
     /// Returns the id of the worktree to which this file belongs.
     ///
@@ -4895,8 +4895,11 @@ impl File for TestFile {
         unimplemented!()
     }
 
-    fn file_name<'a>(&'a self, _: &'a gpui::App) -> &'a std::ffi::OsStr {
-        self.path().file_name().unwrap_or(self.root_name.as_ref())
+    fn file_name<'a>(&'a self, _: &'a gpui::App) -> OsString {
+        self.path()
+            .file_name()
+            .unwrap_or(self.root_name.as_ref())
+            .into()
     }
 
     fn worktree_id(&self, _: &App) -> WorktreeId {

crates/language_model/src/registry.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 };
 use collections::BTreeMap;
 use gpui::{App, Context, Entity, EventEmitter, Global, prelude::*};
-use std::{str::FromStr, sync::Arc};
+use std::{cell::Ref, str::FromStr, sync::Arc};
 use thiserror::Error;
 use util::maybe;
 
@@ -119,7 +119,7 @@ impl LanguageModelRegistry {
         cx.global::<GlobalLanguageModelRegistry>().0.clone()
     }
 
-    pub fn read_global(cx: &App) -> &Self {
+    pub fn read_global(cx: &App) -> Ref<Self> {
         cx.global::<GlobalLanguageModelRegistry>().0.read(cx)
     }
 

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -1215,8 +1215,9 @@ impl MultiBuffer {
                     if let Some(excerpt) = cursor.item() {
                         if excerpt.locator == *excerpt_id {
                             let excerpt_buffer_start =
-                                excerpt.range.context.start.summary::<D>(buffer);
-                            let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(buffer);
+                                excerpt.range.context.start.summary::<D>(&buffer);
+                            let excerpt_buffer_end =
+                                excerpt.range.context.end.summary::<D>(&buffer);
                             let excerpt_range = excerpt_buffer_start..excerpt_buffer_end;
                             if excerpt_range.contains(&range.start)
                                 && excerpt_range.contains(&range.end)
@@ -2477,7 +2478,7 @@ impl MultiBuffer {
         };
 
         let buffer = buffer_state.buffer.read(cx);
-        let diff_change_range = range.to_offset(buffer);
+        let diff_change_range = range.to_offset(&buffer);
 
         let new_diff = diff.snapshot(cx);
         let mut snapshot = self.snapshot.borrow_mut();
@@ -2558,19 +2559,20 @@ impl MultiBuffer {
     }
 
     pub fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
-        let buffer_id = self
-            .snapshot
-            .borrow()
-            .excerpts
-            .first()
-            .map(|excerpt| excerpt.buffer.remote_id());
-        buffer_id
-            .and_then(|buffer_id| self.buffer(buffer_id))
-            .map(|buffer| {
-                let buffer = buffer.read(cx);
-                language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
-            })
-            .unwrap_or_else(move || self.language_settings_at(0, cx))
+        // let buffer_id = self
+        //     .snapshot
+        //     .borrow()
+        //     .excerpts
+        //     .first()
+        //     .map(|excerpt| excerpt.buffer.remote_id());
+        // buffer_id
+        //     .and_then(|buffer_id| self.buffer(buffer_id))
+        //     .map(|buffer| {
+        //         let buffer = buffer.read(cx);
+        //         language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
+        //     })
+        //     .unwrap_or_else(move || self.language_settings_at(0, cx))
+        todo!()
     }
 
     pub fn language_settings_at<'a, T: ToOffset>(
@@ -2585,7 +2587,8 @@ impl MultiBuffer {
             language = buffer.language_at(offset);
             file = buffer.file();
         }
-        language_settings(language.map(|l| l.name()), file, cx)
+        // language_settings(language.map(|l| l.name()), file, cx)
+        todo!()
     }
 
     pub fn for_each_buffer(&self, mut f: impl FnMut(&Entity<Buffer>)) {
@@ -2596,23 +2599,24 @@ impl MultiBuffer {
     }
 
     pub fn title<'a>(&'a self, cx: &'a App) -> Cow<'a, str> {
-        if let Some(title) = self.title.as_ref() {
-            return title.into();
-        }
+        // if let Some(title) = self.title.as_ref() {
+        //     return title.into();
+        // }
 
-        if let Some(buffer) = self.as_singleton() {
-            let buffer = buffer.read(cx);
+        // if let Some(buffer) = self.as_singleton() {
+        //     let buffer = buffer.read(cx);
 
-            if let Some(file) = buffer.file() {
-                return file.file_name(cx).to_string_lossy();
-            }
+        //     if let Some(file) = buffer.file() {
+        //         return file.file_name(cx).to_string_lossy();
+        //     }
 
-            if let Some(title) = self.buffer_content_title(buffer) {
-                return title;
-            }
-        };
+        //     if let Some(title) = self.buffer_content_title(&buffer) {
+        //         return title;
+        //     }
+        // };
 
-        "untitled".into()
+        // "untitled".into()
+        todo!()
     }
 
     fn buffer_content_title(&self, buffer: &Buffer) -> Option<Cow<'_, str>> {

crates/project/src/buffer_store.rs 🔗

@@ -126,6 +126,7 @@ impl RemoteBufferStore {
         let version = buffer.version();
         let rpc = self.upstream_client.clone();
         let project_id = self.project_id;
+        drop(buffer);
         cx.spawn(async move |_, cx| {
             let response = rpc
                 .request(proto::SaveBuffer {
@@ -373,6 +374,7 @@ impl LocalBufferStore {
         let save = worktree.update(cx, |worktree, cx| {
             worktree.write_file(path.as_ref(), text, line_ending, cx)
         });
+        drop(buffer);
 
         cx.spawn(async move |this, cx| {
             let new_file = save.await?;
@@ -574,7 +576,8 @@ impl LocalBufferStore {
         buffer: Entity<Buffer>,
         cx: &mut Context<BufferStore>,
     ) -> Task<Result<()>> {
-        let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
+        let buffer_ref = buffer.read(cx);
+        let Some(file) = File::from_dyn(buffer_ref.file()) else {
             return Task::ready(Err(anyhow!("buffer doesn't have a file")));
         };
         let worktree = file.worktree.clone();
@@ -922,6 +925,7 @@ impl BufferStore {
             self.path_to_buffer_id.insert(path, remote_id);
         }
 
+        drop(buffer);
         cx.subscribe(&buffer_entity, Self::on_buffer_event).detach();
         cx.emit(BufferStoreEvent::BufferAdded(buffer_entity));
         Ok(())

crates/project/src/project.rs 🔗

@@ -1740,7 +1740,8 @@ impl Project {
     }
 
     pub fn active_debug_session(&self, cx: &App) -> Option<(Entity<Session>, ActiveStackFrame)> {
-        let active_position = self.breakpoint_store.read(cx).active_position()?;
+        let store = self.breakpoint_store.read(cx);
+        let active_position = store.active_position()?;
         let session = self
             .dap_store
             .read(cx)

crates/project/src/project_tests.rs 🔗

@@ -8162,8 +8162,8 @@ async fn test_update_gitignore(cx: &mut gpui::TestAppContext) {
 
     // One file is unmodified, the other is ignored.
     cx.read(|cx| {
-        assert_entry_git_state(tree.read(cx), repository.read(cx), "a.xml", None, false);
-        assert_entry_git_state(tree.read(cx), repository.read(cx), "b.txt", None, true);
+        assert_entry_git_state(&tree.read(cx), &repository.read(cx), "a.xml", None, false);
+        assert_entry_git_state(&tree.read(cx), &repository.read(cx), "b.txt", None, true);
     });
 
     // Change the gitignore, and stage the newly non-ignored file.
@@ -8181,10 +8181,10 @@ async fn test_update_gitignore(cx: &mut gpui::TestAppContext) {
 
     cx.executor().run_until_parked();
     cx.read(|cx| {
-        assert_entry_git_state(tree.read(cx), repository.read(cx), "a.xml", None, true);
+        assert_entry_git_state(&tree.read(cx), &repository.read(cx), "a.xml", None, true);
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "b.txt",
             Some(StatusCode::Added),
             false,
@@ -8597,22 +8597,22 @@ async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) {
 
     cx.read(|cx| {
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "tracked-dir/tracked-file1",
             None,
             false,
         );
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "tracked-dir/ancestor-ignored-file1",
             None,
             false,
         );
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "ignored-dir/ignored-file1",
             None,
             true,
@@ -8649,22 +8649,22 @@ async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) {
     cx.executor().run_until_parked();
     cx.read(|cx| {
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "tracked-dir/tracked-file2",
             Some(StatusCode::Added),
             false,
         );
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "tracked-dir/ancestor-ignored-file2",
             None,
             false,
         );
         assert_entry_git_state(
-            tree.read(cx),
-            repository.read(cx),
+            &tree.read(cx),
+            &repository.read(cx),
             "ignored-dir/ignored-file2",
             None,
             true,

crates/project/src/task_inventory.rs 🔗

@@ -435,7 +435,7 @@ impl Inventory {
         let fs = self.fs.clone();
         let worktree = task_contexts.worktree();
         let location = task_contexts.location();
-        let language = location.and_then(|location| location.buffer.read(cx).language());
+        let language = location.and_then(|location| location.buffer.read(cx).language().clone());
         let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
             name: language.name().into(),
         });

crates/web_search/src/web_search.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::Result;
 use collections::HashMap;
 use gpui::{App, AppContext as _, Context, Entity, Global, SharedString, Task};
-use std::sync::Arc;
+use std::{cell::Ref, sync::Arc};
 use zed_llm_client::WebSearchResponse;
 
 pub fn init(cx: &mut App) {
@@ -32,7 +32,7 @@ impl WebSearchRegistry {
         cx.global::<GlobalWebSearchRegistry>().0.clone()
     }
 
-    pub fn read_global(cx: &App) -> &Self {
+    pub fn read_global(cx: &App) -> Ref<Self> {
         cx.global::<GlobalWebSearchRegistry>().0.read(cx)
     }
 

crates/worktree/src/worktree.rs 🔗

@@ -49,7 +49,7 @@ use std::{
     cmp::Ordering,
     collections::hash_map,
     convert::TryFrom,
-    ffi::OsStr,
+    ffi::{OsStr, OsString},
     fmt,
     future::Future,
     mem::{self},
@@ -2667,17 +2667,18 @@ impl Snapshot {
     }
 
     pub fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
-        let path = path.as_ref();
-        debug_assert!(path.is_relative());
-        self.traverse_from_path(true, true, true, path)
-            .entry()
-            .and_then(|entry| {
-                if entry.path.as_ref() == path {
-                    Some(entry)
-                } else {
-                    None
-                }
-            })
+        // let path = path.as_ref();
+        // debug_assert!(path.is_relative());
+        // self.traverse_from_path(true, true, true, path)
+        //     .entry()
+        //     .and_then(|entry| {
+        //         if entry.path.as_ref() == path {
+        //             Some(entry)
+        //         } else {
+        //             None
+        //         }
+        //     })
+        todo!()
     }
 
     pub fn entry_for_id(&self, id: ProjectEntryId) -> Option<&Entry> {
@@ -3321,10 +3322,11 @@ impl language::File for File {
 
     /// Returns the last component of this handle's absolute path. If this handle refers to the root
     /// of its worktree, then this method will return the name of the worktree itself.
-    fn file_name<'a>(&'a self, cx: &'a App) -> &'a OsStr {
+    fn file_name<'a>(&'a self, cx: &'a App) -> OsString {
         self.path
             .file_name()
-            .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name))
+            .map(Into::into)
+            .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name).into())
     }
 
     fn worktree_id(&self, cx: &App) -> WorktreeId {
@@ -3357,14 +3359,16 @@ impl language::LocalFile for File {
     }
 
     fn load(&self, cx: &App) -> Task<Result<String>> {
-        let worktree = self.worktree.read(cx).as_local().unwrap();
+        let worktree = self.worktree.read(cx);
+        let worktree = worktree.as_local().unwrap();
         let abs_path = worktree.absolutize(&self.path);
         let fs = worktree.fs.clone();
         cx.background_spawn(async move { fs.load(&abs_path?).await })
     }
 
     fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>> {
-        let worktree = self.worktree.read(cx).as_local().unwrap();
+        let worktree = self.worktree.read(cx);
+        let worktree = worktree.as_local().unwrap();
         let abs_path = worktree.absolutize(&self.path);
         let fs = worktree.fs.clone();
         cx.background_spawn(async move { fs.load_bytes(&abs_path?).await })