WIP

Nathan Sobo created

Change summary

crates/project/src/buffer_store.rs            |  27 +
crates/project/src/debugger/dap_store.rs      |   2 
crates/project/src/git_store.rs               |  53 ++-
crates/project/src/git_store/conflict_set.rs  |   4 
crates/project/src/git_store/git_traversal.rs |  20 
crates/project/src/lsp_command.rs             |   4 
crates/project/src/lsp_store.rs               | 297 +++++++++++---------
crates/project/src/manifest_tree.rs           |   4 
crates/project/src/project.rs                 | 192 +++++++++----
crates/project/src/project_tests.rs           |  42 +-
crates/project/src/task_inventory.rs          |  20 
crates/project/src/terminals.rs               |   9 
crates/project/src/toolchain_store.rs         |  11 
crates/project/src/worktree_store.rs          |  43 ++
14 files changed, 445 insertions(+), 283 deletions(-)

Detailed changes

crates/project/src/buffer_store.rs 🔗

@@ -576,12 +576,14 @@ impl LocalBufferStore {
         buffer: Entity<Buffer>,
         cx: &mut Context<BufferStore>,
     ) -> Task<Result<()>> {
-        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, path) = {
+            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")));
+            };
+            (file.worktree.clone(), file.path.clone())
         };
-        let worktree = file.worktree.clone();
-        self.save_local_buffer(buffer, worktree, file.path.clone(), false, cx)
+        self.save_local_buffer(buffer, worktree, path, false, cx)
     }
 
     fn save_buffer_as(
@@ -1026,9 +1028,9 @@ impl BufferStore {
     }
 
     fn buffer_changed_file(&mut self, buffer: Entity<Buffer>, cx: &mut App) -> Option<()> {
-        let file = File::from_dyn(buffer.read(cx).file())?;
-
-        let remote_id = buffer.read(cx).remote_id();
+        let buffer_ref = buffer.read(cx);
+        let file = File::from_dyn(buffer_ref.file())?;
+        let remote_id = buffer_ref.remote_id();
         if let Some(entry_id) = file.entry_id {
             if let Some(local) = self.as_local_mut() {
                 match local.local_buffer_ids_by_entry_id.get(&entry_id) {
@@ -1065,10 +1067,13 @@ impl BufferStore {
         let mut open_buffers = HashSet::default();
         let mut unnamed_buffers = Vec::new();
         for handle in self.buffers() {
-            let buffer = handle.read(cx);
-            if self.non_searchable_buffers.contains(&buffer.remote_id()) {
+            let (remote_id, entry_id) = {
+                let buffer = handle.read(cx);
+                (buffer.remote_id(), buffer.entry_id(cx))
+            };
+            if self.non_searchable_buffers.contains(&remote_id) {
                 continue;
-            } else if let Some(entry_id) = buffer.entry_id(cx) {
+            } else if let Some(entry_id) = entry_id {
                 open_buffers.insert(entry_id);
             } else {
                 limit = limit.saturating_sub(1);

crates/project/src/debugger/dap_store.rs 🔗

@@ -155,7 +155,7 @@ impl DapStore {
     ) -> Self {
         let mode = DapStoreMode::Ssh(SshDapStore {
             upstream_client: ssh_client.read(cx).proto_client(),
-            ssh_client,
+            ssh_client: ssh_client.clone(),
             upstream_project_id: project_id,
         });
 

crates/project/src/git_store.rs 🔗

@@ -947,14 +947,18 @@ impl GitStore {
         selection: Range<u32>,
         cx: &mut App,
     ) -> Task<Result<url::Url>> {
-        let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
-            return Task::ready(Err(anyhow!("buffer has no file")));
+        let (worktree, path) = {
+            let buffer_ref = buffer.read(cx);
+            let Some(file) = File::from_dyn(buffer_ref.file()) else {
+                return Task::ready(Err(anyhow!("buffer has no file")));
+            };
+            (file.worktree.clone(), file.path.clone())
         };
 
-        let Some((repo, repo_path)) = self.repository_and_path_for_project_path(
-            &(file.worktree.read(cx).id(), file.path.clone()).into(),
-            cx,
-        ) else {
+        let worktree_id = worktree.read(cx).id();
+        let Some((repo, repo_path)) =
+            self.repository_and_path_for_project_path(&(worktree_id, path.clone()).into(), cx)
+        else {
             // If we're not in a Git repo, check whether this is a Rust source
             // file in the Cargo registry (presumably opened with go-to-definition
             // from a normal Rust file). If so, we can put together a permalink
@@ -966,7 +970,7 @@ impl GitStore {
             {
                 return Task::ready(Err(anyhow!("no permalink available")));
             }
-            let Some(file_path) = file.worktree.read(cx).absolutize(&file.path).ok() else {
+            let Some(file_path) = worktree.read(cx).absolutize(&path).ok() else {
                 return Task::ready(Err(anyhow!("no permalink available")));
             };
             return cx.spawn(async move |cx| {
@@ -2062,7 +2066,7 @@ impl GitStore {
                 .or_default();
             shared_diffs.entry(buffer_id).or_default().uncommitted = Some(diff.clone());
         })?;
-        diff.read_with(&cx, |diff, cx| {
+        Ok(diff.read_with(&cx, |diff, cx| {
             use proto::open_uncommitted_diff_response::Mode;
 
             let unstaged_diff = diff.secondary_diff();
@@ -2076,14 +2080,14 @@ impl GitStore {
             let committed_text;
             if diff.base_text_exists() {
                 let committed_snapshot = diff.base_text();
-                committed_text = Some(committed_snapshot.text());
+                committed_text = Some(committed_snapshot.text().to_string());
                 if let Some(index_text) = index_snapshot {
                     if index_text.remote_id() == committed_snapshot.remote_id() {
                         mode = Mode::IndexMatchesHead;
                         staged_text = None;
                     } else {
                         mode = Mode::IndexAndHead;
-                        staged_text = Some(index_text.text());
+                        staged_text = Some(index_text.text().to_string());
                     }
                 } else {
                     mode = Mode::IndexAndHead;
@@ -2092,15 +2096,17 @@ impl GitStore {
             } else {
                 mode = Mode::IndexAndHead;
                 committed_text = None;
-                staged_text = index_snapshot.as_ref().map(|buffer| buffer.text());
+                staged_text = index_snapshot
+                    .as_ref()
+                    .map(|buffer| buffer.text().to_string());
             }
 
             proto::OpenUncommittedDiffResponse {
                 committed_text,
                 staged_text,
-                mode: mode.into(),
+                mode: mode as i32,
             }
-        })
+        })?)
     }
 
     async fn handle_update_diff_bases(
@@ -2842,9 +2848,15 @@ impl Repository {
                             .filter_map(|(buffer_id, diff_state)| {
                                 let buffer_store = git_store.buffer_store.read(cx);
                                 let buffer = buffer_store.get(*buffer_id)?;
-                                let file = File::from_dyn(buffer.read(cx).file())?;
-                                let abs_path =
-                                    file.worktree.read(cx).absolutize(&file.path).ok()?;
+                                let (worktree, path) = {
+                                    let buffer_ref = buffer.read(cx);
+                                    let file = File::from_dyn(buffer_ref.file())?;
+                                    (file.worktree.clone(), file.path.clone())
+                                };
+                                let abs_path = {
+                                    let worktree_ref = worktree.read(cx);
+                                    worktree_ref.absolutize(&path).ok()?
+                                };
                                 let repo_path = this.abs_path_to_repo_path(&abs_path)?;
                                 log::debug!(
                                     "start reload diff bases for repo path {}",
@@ -3066,18 +3078,21 @@ impl Repository {
 
     pub fn repo_path_to_project_path(&self, path: &RepoPath, cx: &App) -> Option<ProjectPath> {
         let git_store = self.git_store.upgrade()?;
-        let worktree_store = git_store.read(cx).worktree_store.read(cx);
+        let git_store_ref = git_store.read(cx);
+        let worktree_store = git_store_ref.worktree_store.read(cx);
         let abs_path = self.snapshot.work_directory_abs_path.join(&path.0);
         let (worktree, relative_path) = worktree_store.find_worktree(abs_path, cx)?;
+        let worktree_id = worktree.read(cx).id();
         Some(ProjectPath {
-            worktree_id: worktree.read(cx).id(),
+            worktree_id,
             path: relative_path.into(),
         })
     }
 
     pub fn project_path_to_repo_path(&self, path: &ProjectPath, cx: &App) -> Option<RepoPath> {
         let git_store = self.git_store.upgrade()?;
-        let worktree_store = git_store.read(cx).worktree_store.read(cx);
+        let git_store_ref = git_store.read(cx);
+        let worktree_store = git_store_ref.worktree_store.read(cx);
         let abs_path = worktree_store.absolutize(path, cx)?;
         self.snapshot.abs_path_to_repo_path(&abs_path)
     }

crates/project/src/git_store/conflict_set.rs 🔗

@@ -641,7 +641,7 @@ mod tests {
         conflict_set.update(cx, |conflict_set, cx| {
             let conflict_range = conflict_set.snapshot().conflicts[0]
                 .range
-                .to_point(buffer.read(cx));
+                .to_point(&buffer.read(cx).snapshot());
             assert_eq!(conflict_range, Point::new(1, 0)..Point::new(6, 0));
         });
 
@@ -673,7 +673,7 @@ mod tests {
         conflict_set.update(cx, |conflict_set, cx| {
             let conflict_range = conflict_set.snapshot().conflicts[0]
                 .range
-                .to_point(buffer.read(cx));
+                .to_point(&buffer.read(cx).snapshot());
             assert_eq!(conflict_range, Point::new(1, 0)..Point::new(6, 0));
         });
     }

crates/project/src/git_store/git_traversal.rs 🔗

@@ -710,11 +710,11 @@ mod tests {
         cx.executor().run_until_parked();
 
         let (old_entry_ids, old_mtimes) = project.read_with(cx, |project, cx| {
-            let tree = project.worktrees(cx).next().unwrap().read(cx);
-            (
-                tree.entries(true, 0).map(|e| e.id).collect::<Vec<_>>(),
-                tree.entries(true, 0).map(|e| e.mtime).collect::<Vec<_>>(),
-            )
+            let worktree = project.worktrees(cx).next().unwrap();
+            let tree = worktree.read(cx);
+            let entry_ids = tree.entries(true, 0).map(|e| e.id).collect::<Vec<_>>();
+            let mtimes = tree.entries(true, 0).map(|e| e.mtime).collect::<Vec<_>>();
+            (entry_ids, mtimes)
         });
 
         // Regression test: after the directory is scanned, touch the git repo's
@@ -724,11 +724,11 @@ mod tests {
         cx.executor().run_until_parked();
 
         let (new_entry_ids, new_mtimes) = project.read_with(cx, |project, cx| {
-            let tree = project.worktrees(cx).next().unwrap().read(cx);
-            (
-                tree.entries(true, 0).map(|e| e.id).collect::<Vec<_>>(),
-                tree.entries(true, 0).map(|e| e.mtime).collect::<Vec<_>>(),
-            )
+            let worktree = project.worktrees(cx).next().unwrap();
+            let tree = worktree.read(cx);
+            let entry_ids = tree.entries(true, 0).map(|e| e.id).collect::<Vec<_>>();
+            let mtimes = tree.entries(true, 0).map(|e| e.mtime).collect::<Vec<_>>();
+            (entry_ids, mtimes)
         });
         assert_eq!(new_entry_ids, old_entry_ids);
         assert_ne!(new_mtimes, old_mtimes);

crates/project/src/lsp_command.rs 🔗

@@ -1151,7 +1151,7 @@ pub async fn location_links_from_lsp(
             let target_end =
                 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
             let target_location = Location {
-                buffer: target_buffer_handle,
+                buffer: target_buffer_handle.clone(),
                 range: target_buffer.anchor_after(target_start)
                     ..target_buffer.anchor_before(target_end),
             };
@@ -1212,7 +1212,7 @@ pub async fn location_link_from_lsp(
         let target_end =
             target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
         let target_location = Location {
-            buffer: target_buffer_handle,
+            buffer: target_buffer_handle.clone(),
             range: target_buffer.anchor_after(target_start)
                 ..target_buffer.anchor_before(target_end),
         };

crates/project/src/lsp_store.rs 🔗

@@ -2276,10 +2276,9 @@ impl LocalLspStore {
 
         let snapshot = self.buffer_snapshot_for_lsp_version(buffer, server_id, version, cx)?;
 
-        let edits_since_save = std::cell::LazyCell::new(|| {
-            let saved_version = buffer.read(cx).saved_version();
-            Patch::new(snapshot.edits_since::<PointUtf16>(saved_version).collect())
-        });
+        let saved_version = buffer.read(cx).saved_version();
+        let edits: Vec<_> = snapshot.edits_since::<PointUtf16>(saved_version).collect();
+        let edits_since_save = Patch::new(edits);
 
         let mut sanitized_diagnostics = Vec::with_capacity(diagnostics.len());
 
@@ -2292,8 +2291,8 @@ impl LocalLspStore {
                 // any unsaved edits.
                 // Do not alter the reused ones though, as their coordinates were stored as anchors
                 // and were properly adjusted on reuse.
-                start = Unclipped((*edits_since_save).old_to_new(entry.range.start.0));
-                end = Unclipped((*edits_since_save).old_to_new(entry.range.end.0));
+                start = Unclipped(edits_since_save.old_to_new(entry.range.start.0));
+                end = Unclipped(edits_since_save.old_to_new(entry.range.end.0));
             } else {
                 start = entry.range.start;
                 end = entry.range.end;
@@ -2318,7 +2317,6 @@ impl LocalLspStore {
                 diagnostic: entry.diagnostic,
             });
         }
-        drop(edits_since_save);
 
         let set = DiagnosticSet::new(sanitized_diagnostics, &snapshot);
         buffer.update(cx, |buffer, cx| {
@@ -3228,17 +3226,23 @@ impl LocalLspStore {
         watchers: impl Iterator<Item = &'a FileSystemWatcher>,
         cx: &mut Context<LspStore>,
     ) -> LanguageServerWatchedPathsBuilder {
-        let worktrees = self
+        let worktree_ids = self
             .worktree_store
             .read(cx)
             .worktrees()
             .filter_map(|worktree| {
-                self.language_servers_for_worktree(worktree.read(cx).id())
+                let worktree_id = worktree.read(cx).id();
+                self.language_servers_for_worktree(worktree_id)
                     .find(|server| server.server_id() == language_server_id)
-                    .map(|_| worktree)
+                    .map(|_| worktree_id)
             })
             .collect::<Vec<_>>();
 
+        let worktrees = worktree_ids
+            .into_iter()
+            .filter_map(|id| self.worktree_store.read(cx).worktree_for_id(id, cx))
+            .collect::<Vec<_>>();
+
         let mut worktree_globs = HashMap::default();
         let mut abs_globs = HashMap::default();
         log::trace!(
@@ -3819,7 +3823,7 @@ impl LspStore {
         request: R,
         cx: &mut Context<LspStore>,
     ) -> Task<anyhow::Result<<R as LspCommand>::Response>> {
-        let message = request.to_proto(upstream_project_id, buffer.read(cx));
+        let message = request.to_proto(upstream_project_id, &*buffer.read(cx));
         cx.spawn(async move |this, cx| {
             let response = client.request(message).await?;
             let this = this.upgrade().context("project dropped")?;
@@ -4127,7 +4131,10 @@ impl LspStore {
         ignore_refcounts: bool,
         cx: &mut Context<Self>,
     ) -> OpenLspBufferHandle {
-        let buffer_id = buffer.read(cx).remote_id();
+        let buffer_id = {
+            let buffer_ref = buffer.read(cx);
+            buffer_ref.remote_id()
+        };
         let handle = cx.new(|_| buffer.clone());
         if let Some(local) = self.as_local_mut() {
             let refcount = local.registered_buffers.entry(buffer_id).or_insert(0);
@@ -4139,7 +4146,8 @@ impl LspStore {
             // When a new unnamed buffer is created and saved, we will start loading it's language. Once the language is loaded, we go over all "language-less" buffers and try to fit that new language
             // with them. However, we do that only for the buffers that we think are open in at least one editor; thus, we need to keep tab of unnamed buffers as well, even though they're not actually registered with any language
             // servers in practice (we don't support non-file URI schemes in our LSP impl).
-            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 handle;
             };
             if !file.is_local() {
@@ -4258,12 +4266,18 @@ impl LspStore {
                         let mut plain_text_buffers = Vec::new();
                         let mut buffers_with_unknown_injections = Vec::new();
                         for handle in this.buffer_store.read(cx).buffers() {
-                            let buffer = handle.read(cx);
-                            if buffer.language().is_none()
-                                || buffer.language() == Some(&*language::PLAIN_TEXT)
-                            {
+                            let should_push_plain_text = {
+                                let buffer = handle.read(cx);
+                                buffer.language().is_none()
+                                    || buffer.language() == Some(&*language::PLAIN_TEXT)
+                            };
+                            let contains_unknown_injections = {
+                                let buffer = handle.read(cx);
+                                buffer.contains_unknown_injections()
+                            };
+                            if should_push_plain_text {
                                 plain_text_buffers.push(handle);
-                            } else if buffer.contains_unknown_injections() {
+                            } else if contains_unknown_injections {
                                 buffers_with_unknown_injections.push(handle);
                             }
                         }
@@ -4475,35 +4489,39 @@ impl LspStore {
             return Task::ready(Ok(Default::default()));
         };
 
-        let buffer = buffer_handle.read(cx);
-        let file = File::from_dyn(buffer.file()).and_then(File::as_local);
+        let (abs_path, lsp_params, status) = {
+            let buffer = buffer_handle.read(cx);
+            let file = File::from_dyn(buffer.file()).and_then(File::as_local);
 
-        let Some(file) = file else {
-            return Task::ready(Ok(Default::default()));
-        };
+            let Some(file) = file else {
+                return Task::ready(Ok(Default::default()));
+            };
 
-        let lsp_params = match request.to_lsp_params_or_response(
-            &file.abs_path(cx),
-            buffer,
-            &language_server,
-            cx,
-        ) {
-            Ok(LspParamsOrResponse::Params(lsp_params)) => lsp_params,
-            Ok(LspParamsOrResponse::Response(response)) => return Task::ready(Ok(response)),
+            let abs_path = file.abs_path(cx);
+            let lsp_params = match request.to_lsp_params_or_response(
+                &abs_path,
+                &*buffer,
+                &language_server,
+                cx,
+            ) {
+                Ok(LspParamsOrResponse::Params(lsp_params)) => lsp_params,
+                Ok(LspParamsOrResponse::Response(response)) => return Task::ready(Ok(response)),
+
+                Err(err) => {
+                    let message = format!(
+                        "{} via {} failed: {}",
+                        request.display_name(),
+                        language_server.name(),
+                        err
+                    );
+                    log::warn!("{message}");
+                    return Task::ready(Err(anyhow!(message)));
+                }
+            };
 
-            Err(err) => {
-                let message = format!(
-                    "{} via {} failed: {}",
-                    request.display_name(),
-                    language_server.name(),
-                    err
-                );
-                log::warn!("{message}");
-                return Task::ready(Err(anyhow!(message)));
-            }
+            let status = request.status();
+            (_abs_path, lsp_params, status)
         };
-
-        let status = request.status();
         if !request.check_capabilities(language_server.adapter_server_capabilities()) {
             return Task::ready(Ok(Default::default()));
         }
@@ -5188,7 +5206,7 @@ impl LspStore {
                     })?
                     .await?;
                 this.update(cx, |this, cx| {
-                    let position = position.to_point_utf16(buffer.read(cx));
+                    let position = position.to_point_utf16(&buffer.read(cx).snapshot());
                     this.on_type_format(buffer, position, trigger, false, cx)
                 })?
                 .await
@@ -5206,7 +5224,7 @@ impl LspStore {
         push_to_history: bool,
         cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.on_type_format_impl(buffer, position, trigger, push_to_history, cx)
     }
 
@@ -5269,7 +5287,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetDefinition(
-                    GetDefinitions { position }.to_proto(project_id, buffer_handle.read(cx)),
+                    GetDefinitions { position }.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5342,7 +5360,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetDeclaration(
-                    GetDeclarations { position }.to_proto(project_id, buffer_handle.read(cx)),
+                    GetDeclarations { position }.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5415,7 +5433,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetTypeDefinition(
-                    GetTypeDefinitions { position }.to_proto(project_id, buffer_handle.read(cx)),
+                    GetTypeDefinitions { position }.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5488,7 +5506,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetImplementation(
-                    GetImplementations { position }.to_proto(project_id, buffer_handle.read(cx)),
+                    GetImplementations { position }.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5561,7 +5579,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetReferences(
-                    GetReferences { position }.to_proto(project_id, buffer_handle.read(cx)),
+                    GetReferences { position }.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5639,7 +5657,7 @@ impl LspStore {
                         range: range.clone(),
                         kinds: kinds.clone(),
                     }
-                    .to_proto(project_id, buffer_handle.read(cx)),
+                    .to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -5716,7 +5734,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetCodeLens(
-                    GetCodeLens.to_proto(project_id, buffer_handle.read(cx)),
+                    GetCodeLens.to_proto(project_id, &*buffer_handle.read(cx)),
                 )),
             });
             let buffer = buffer_handle.clone();
@@ -6278,8 +6296,7 @@ impl LspStore {
         cx: &mut Context<Self>,
     ) -> Task<Result<Option<Transaction>>> {
         if let Some((client, project_id)) = self.upstream_client() {
-            let buffer = buffer_handle.read(cx);
-            let buffer_id = buffer.remote_id();
+            let buffer_id = buffer_handle.read(cx).remote_id();
             cx.spawn(async move |_, cx| {
                 let request = {
                     let completion = completions.borrow()[completion_index].clone();
@@ -6482,6 +6499,7 @@ impl LspStore {
                 end: Some(serialize_anchor(&range_end)),
                 version: serialize_version(&buffer_handle.read(cx).version()),
             };
+            let buffer_handle_clone = buffer_handle.clone();
             cx.spawn(async move |project, cx| {
                 let response = client
                     .request(request)
@@ -6491,13 +6509,14 @@ impl LspStore {
                     lsp_request,
                     response,
                     project.upgrade().context("No project")?,
-                    buffer_handle.clone(),
+                    buffer_handle_clone,
                     cx.clone(),
                 )
                 .await
                 .context("inlay hints proto response conversion")
             })
         } else {
+            let buffer_handle_for_task = buffer_handle.clone();
             let lsp_request_task = self.request_lsp(
                 buffer_handle.clone(),
                 LanguageServerToQuery::FirstCapable,
@@ -6505,7 +6524,7 @@ impl LspStore {
                 cx,
             );
             cx.spawn(async move |_, cx| {
-                buffer_handle
+                buffer_handle_for_task
                     .update(cx, |buffer, _| {
                         buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
                     })?
@@ -6729,17 +6748,20 @@ impl LspStore {
         cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<HashMap<LanguageServerId, HashSet<DocumentColor>>>> {
         if let Some((client, project_id)) = self.upstream_client() {
-            let request_task = client.request(proto::MultiLspQuery {
-                project_id,
-                buffer_id: buffer.read(cx).remote_id().to_proto(),
-                version: serialize_version(&buffer.read(cx).version()),
-                strategy: Some(proto::multi_lsp_query::Strategy::All(
-                    proto::AllLanguageServers {},
-                )),
-                request: Some(proto::multi_lsp_query::Request::GetDocumentColor(
-                    GetDocumentColor {}.to_proto(project_id, buffer.read(cx)),
-                )),
-            });
+            let request_task = {
+                let buffer_ref = buffer.read(cx);
+                client.request(proto::MultiLspQuery {
+                    project_id,
+                    buffer_id: buffer_ref.remote_id().to_proto(),
+                    version: serialize_version(&buffer_ref.version()),
+                    strategy: Some(proto::multi_lsp_query::Strategy::All(
+                        proto::AllLanguageServers {},
+                    )),
+                    request: Some(proto::multi_lsp_query::Request::GetDocumentColor(
+                        GetDocumentColor.to_proto(project_id, &*buffer_ref),
+                    )),
+                })
+            };
             cx.spawn(async move |project, cx| {
                 let Some(project) = project.upgrade() else {
                     return Ok(HashMap::default());
@@ -6808,7 +6830,7 @@ impl LspStore {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Vec<SignatureHelp>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
 
         if let Some((client, upstream_project_id)) = self.upstream_client() {
             let request_task = client.request(proto::MultiLspQuery {
@@ -6819,7 +6841,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetSignatureHelp(
-                    GetSignatureHelp { position }.to_proto(upstream_project_id, buffer.read(cx)),
+                    GetSignatureHelp { position }.to_proto(upstream_project_id, &*buffer.read(cx)),
                 )),
             });
             let buffer = buffer.clone();
@@ -6890,7 +6912,7 @@ impl LspStore {
                     proto::AllLanguageServers {},
                 )),
                 request: Some(proto::multi_lsp_query::Request::GetHover(
-                    GetHover { position }.to_proto(upstream_project_id, buffer.read(cx)),
+                    GetHover { position }.to_proto(upstream_project_id, &*buffer.read(cx)),
                 )),
             });
             let buffer = buffer.clone();
@@ -7141,41 +7163,49 @@ impl LspStore {
         summary
     }
 
+    // pub fn diagnostic_summaries<'a>(
+    //     &'a self,
+    //     include_ignored: bool,
+    //     cx: &'a App,
+    // ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
+    //     self.worktree_store
+    //         .read(cx)
+    //         .visible_worktrees(cx)
+    //         .filter_map(|worktree| {
+    //             let worktree = worktree.read(cx);
+    //             Some((worktree, self.diagnostic_summaries.get(&worktree.id())?))
+    //         })
+    //         .flat_map(move |(worktree, summaries)| {
+    //             let worktree_id = worktree.id();
+    //             summaries
+    //                 .iter()
+    //                 .filter(move |(path, _)| {
+    //                     include_ignored
+    //                         || worktree
+    //                             .entry_for_path(path.as_ref())
+    //                             .map_or(false, |entry| !entry.is_ignored)
+    //                 })
+    //                 .flat_map(move |(path, summaries)| {
+    //                     summaries.iter().map(move |(server_id, summary)| {
+    //                         (
+    //                             ProjectPath {
+    //                                 worktree_id,
+    //                                 path: path.clone(),
+    //                             },
+    //                             *server_id,
+    //                             *summary,
+    //                         )
+    //                     })
+    //                 })
+    //         })
+    // }
     pub fn diagnostic_summaries<'a>(
         &'a self,
         include_ignored: bool,
         cx: &'a App,
     ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
-        self.worktree_store
-            .read(cx)
-            .visible_worktrees(cx)
-            .filter_map(|worktree| {
-                let worktree = worktree.read(cx);
-                Some((worktree, self.diagnostic_summaries.get(&worktree.id())?))
-            })
-            .flat_map(move |(worktree, summaries)| {
-                let worktree_id = worktree.id();
-                summaries
-                    .iter()
-                    .filter(move |(path, _)| {
-                        include_ignored
-                            || worktree
-                                .entry_for_path(path.as_ref())
-                                .map_or(false, |entry| !entry.is_ignored)
-                    })
-                    .flat_map(move |(path, summaries)| {
-                        summaries.iter().map(move |(server_id, summary)| {
-                            (
-                                ProjectPath {
-                                    worktree_id,
-                                    path: path.clone(),
-                                },
-                                *server_id,
-                                *summary,
-                            )
-                        })
-                    })
-            })
+        // todo!("diagnostic_summaries needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn on_buffer_edited(
@@ -7288,7 +7318,8 @@ impl LspStore {
         buffer: Entity<Buffer>,
         cx: &mut Context<Self>,
     ) -> Option<()> {
-        let file = File::from_dyn(buffer.read(cx).file())?;
+        let buffer_ref = buffer.read(cx);
+        let file = File::from_dyn(buffer_ref.file())?;
         let worktree_id = file.worktree_id(cx);
         let abs_path = file.as_local()?.abs_path(cx);
         let text_document = lsp::TextDocumentIdentifier {
@@ -7610,7 +7641,8 @@ impl LspStore {
             path: relative_path.into(),
         };
 
-        if let Some(buffer_handle) = self.buffer_store.read(cx).get_by_path(&project_path) {
+        let buffer_handle = self.buffer_store.read(cx).get_by_path(&project_path);
+        if let Some(buffer_handle) = buffer_handle {
             let snapshot = buffer_handle.read(cx).snapshot();
             let buffer = buffer_handle.read(cx);
             let reused_diagnostics = buffer
@@ -7618,7 +7650,7 @@ impl LspStore {
                 .into_iter()
                 .flat_map(|diag| {
                     diag.iter()
-                        .filter(|v| filter(buffer, &v.diagnostic, cx))
+                        .filter(|v| filter(&*buffer, &v.diagnostic, cx))
                         .map(|v| {
                             let start = Unclipped(v.range.start.to_point_utf16(&snapshot));
                             let end = Unclipped(v.range.end.to_point_utf16(&snapshot));
@@ -7629,6 +7661,7 @@ impl LspStore {
                         })
                 })
                 .collect::<Vec<_>>();
+            drop(buffer);
 
             self.as_local_mut()
                 .context("cannot merge diagnostics on a remote LspStore")?
@@ -8570,12 +8603,8 @@ impl LspStore {
                     .read(cx)
                     .worktree_and_entry_for_id(entry_id, cx)
                     .map(|(worktree, entry)| {
-                        (
-                            worktree.read(cx).id(),
-                            worktree,
-                            entry.path.clone(),
-                            entry.is_dir(),
-                        )
+                        let worktree_id = worktree.read(cx).id();
+                        (worktree_id, worktree, entry.path.clone(), entry.is_dir())
                     })
             })?
             .context("worktree not found")?;
@@ -9862,11 +9891,14 @@ impl LspStore {
             let buffers = buffers
                 .into_iter()
                 .map(|buffer_handle| {
-                    let buffer = buffer_handle.read(cx);
-                    let buffer_abs_path = File::from_dyn(buffer.file())
-                        .and_then(|file| file.as_local().map(|f| f.abs_path(cx)));
+                    let (buffer_abs_path, remote_id) = {
+                        let buffer = buffer_handle.read(cx);
+                        let buffer_abs_path = File::from_dyn(buffer.file())
+                            .and_then(|file| file.as_local().map(|f| f.abs_path(cx)));
+                        (buffer_abs_path, buffer.remote_id())
+                    };
 
-                    (buffer_handle, buffer_abs_path, buffer.remote_id())
+                    (buffer_handle, buffer_abs_path, remote_id)
                 })
                 .collect::<Vec<_>>();
 
@@ -10393,21 +10425,24 @@ impl LspStore {
         cx.background_spawn(futures::future::join_all(tasks).map(|_| ()))
     }
 
-    fn get_buffer<'a>(&self, abs_path: &Path, cx: &'a App) -> Option<&'a Buffer> {
-        let (worktree, relative_path) =
-            self.worktree_store.read(cx).find_worktree(&abs_path, cx)?;
+    // fn get_buffer<'a>(&self, abs_path: &Path, cx: &'a App) -> Option<&'a Buffer> {
+    //     let (worktree, relative_path) =
+    //         self.worktree_store.read(cx).find_worktree(&abs_path, cx)?;
 
-        let project_path = ProjectPath {
-            worktree_id: worktree.read(cx).id(),
-            path: relative_path.into(),
-        };
+    //     let project_path = ProjectPath {
+    //         worktree_id: worktree.read(cx).id(),
+    //         path: relative_path.into(),
+    //     };
 
-        Some(
-            self.buffer_store()
-                .read(cx)
-                .get_by_path(&project_path)?
-                .read(cx),
-        )
+    //     Some(
+    //         self.buffer_store()
+    //             .read(cx)
+    //             .get_by_path(&project_path)?
+    //             .read(cx),
+    //     )
+    // }
+    fn get_buffer<'a>(&self, _abs_path: &Path, _cx: &'a App) -> Option<&'a Buffer> {
+        todo!("get_buffer needs to be refactored to handle Ref type")
     }
 
     pub fn update_diagnostics(
@@ -11239,12 +11274,10 @@ impl LspStore {
         buffer_id: BufferId,
         cx: &App,
     ) -> Option<String> {
-        let abs_path = self
-            .buffer_store
-            .read(cx)
-            .get(buffer_id)
-            .and_then(|b| File::from_dyn(b.read(cx).file()))
-            .map(|f| f.abs_path(cx))?;
+        let abs_path = self.buffer_store.read(cx).get(buffer_id).and_then(|b| {
+            let buffer_ref = b.read(cx);
+            File::from_dyn(buffer_ref.file()).map(|f| f.abs_path(cx).to_path_buf())
+        })?;
         self.as_local()?
             .buffer_pull_diagnostics_result_ids
             .get(&server_id)?

crates/project/src/manifest_tree.rs 🔗

@@ -62,8 +62,8 @@ impl WorktreeRoots {
                     }
                     WorktreeEvent::UpdatedGitRepositories(_) => {}
                     WorktreeEvent::DeletedEntry(entry_id) => {
-                        let Some(entry) = this.worktree_store.read(cx).entry_for_id(*entry_id, cx)
-                        else {
+                        let worktree_store = this.worktree_store.read(cx);
+                        let Some(entry) = worktree_store.entry_for_id(*entry_id, cx) else {
                             return;
                         };
                         let path = TriePath::from(entry.path.as_ref());

crates/project/src/project.rs 🔗

@@ -1818,11 +1818,18 @@ impl Project {
         })
     }
 
+    // pub fn shell_environment_errors<'a>(
+    //     &'a self,
+    //     cx: &'a App,
+    // ) -> impl Iterator<Item = (&'a Arc<Path>, &'a EnvironmentErrorMessage)> {
+    //     self.environment.read(cx).environment_errors()
+    // }
     pub fn shell_environment_errors<'a>(
         &'a self,
         cx: &'a App,
     ) -> impl Iterator<Item = (&'a Arc<Path>, &'a EnvironmentErrorMessage)> {
-        self.environment.read(cx).environment_errors()
+        // todo!("shell_environment_errors needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn remove_environment_error(&mut self, abs_path: &Path, cx: &mut Context<Self>) {
@@ -1933,20 +1940,36 @@ impl Project {
         });
     }
 
+    // /// Collect all worktrees, including ones that don't appear in the project panel
+    // pub fn worktrees<'a>(
+    //     &self,
+    //     cx: &'a App,
+    // ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
+    //     self.worktree_store.read(cx).worktrees()
+    // }
     /// Collect all worktrees, including ones that don't appear in the project panel
     pub fn worktrees<'a>(
         &self,
         cx: &'a App,
     ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
-        self.worktree_store.read(cx).worktrees()
+        // todo!("worktrees needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
+    // /// Collect all user-visible worktrees, the ones that appear in the project panel.
+    // pub fn visible_worktrees<'a>(
+    //     &'a self,
+    //     cx: &'a App,
+    // ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
+    //     self.worktree_store.read(cx).visible_worktrees(cx)
+    // }
     /// Collect all user-visible worktrees, the ones that appear in the project panel.
     pub fn visible_worktrees<'a>(
         &'a self,
         cx: &'a App,
     ) -> impl 'a + DoubleEndedIterator<Item = Entity<Worktree>> {
-        self.worktree_store.read(cx).visible_worktrees(cx)
+        // todo!("visible_worktrees needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn worktree_for_root_name(&self, root_name: &str, cx: &App) -> Option<Entity<Worktree>> {
@@ -1954,9 +1977,13 @@ impl Project {
             .find(|tree| tree.read(cx).root_name() == root_name)
     }
 
+    // pub fn worktree_root_names<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = &'a str> {
+    //     self.visible_worktrees(cx)
+    //         .map(|tree| tree.read(cx).root_name())
+    // }
     pub fn worktree_root_names<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = &'a str> {
-        self.visible_worktrees(cx)
-            .map(|tree| tree.read(cx).root_name())
+        // todo!("worktree_root_names needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn worktree_for_id(&self, id: WorktreeId, cx: &App) -> Option<Entity<Worktree>> {
@@ -3307,15 +3334,26 @@ impl Project {
             .read(cx)
             .active_toolchain(path, language_name, cx)
     }
+    // pub fn language_server_statuses<'a>(
+    //     &'a self,
+    //     cx: &'a App,
+    // ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &'a LanguageServerStatus)> {
+    //     self.lsp_store.read(cx).language_server_statuses()
+    // }
     pub fn language_server_statuses<'a>(
         &'a self,
         cx: &'a App,
     ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &'a LanguageServerStatus)> {
-        self.lsp_store.read(cx).language_server_statuses()
+        // todo!("language_server_statuses needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
-    pub fn last_formatting_failure<'a>(&self, cx: &'a App) -> Option<&'a str> {
-        self.lsp_store.read(cx).last_formatting_failure()
+    // pub fn last_formatting_failure<'a>(&self, cx: &'a App) -> Option<&'a str> {
+    //     self.lsp_store.read(cx).last_formatting_failure()
+    // }
+    pub fn last_formatting_failure<'a>(&self, _cx: &'a App) -> Option<&'a str> {
+        // todo!("last_formatting_failure needs to be refactored to handle Ref type")
+        None
     }
 
     pub fn reset_last_formatting_failure(&self, cx: &mut App) {
@@ -3362,7 +3400,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<LocationLink>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.definitions(buffer, position, cx)
         })
@@ -3374,7 +3412,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<LocationLink>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.declarations(buffer, position, cx)
         })
@@ -3386,7 +3424,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<LocationLink>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.type_definitions(buffer, position, cx)
         })
@@ -3398,7 +3436,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<LocationLink>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.implementations(buffer, position, cx)
         })
@@ -3410,7 +3448,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<Location>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.references(buffer, position, cx)
         })
@@ -3436,7 +3474,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<DocumentHighlight>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.document_highlights_impl(buffer, position, cx)
     }
 
@@ -3533,7 +3571,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Vec<Hover>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store
             .update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
     }
@@ -3556,7 +3594,7 @@ impl Project {
         context: CompletionContext,
         cx: &mut Context<Self>,
     ) -> Task<Result<Vec<CompletionResponse>>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.completions(buffer, position, context, cx)
         })
@@ -3647,7 +3685,7 @@ impl Project {
         position: T,
         cx: &mut Context<Self>,
     ) -> Task<Result<PrepareRenameResponse>> {
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.prepare_rename_impl(buffer, position, cx)
     }
 
@@ -3659,7 +3697,7 @@ impl Project {
         cx: &mut Context<Self>,
     ) -> Task<Result<ProjectTransaction>> {
         let push_to_history = true;
-        let position = position.to_point_utf16(buffer.read(cx));
+        let position = position.to_point_utf16(&buffer.read(cx).snapshot());
         self.request_lsp(
             buffer,
             LanguageServerToQuery::FirstCapable,
@@ -3726,8 +3764,10 @@ impl Project {
         range: Range<T>,
         cx: &mut Context<Self>,
     ) -> Task<anyhow::Result<Vec<InlayHint>>> {
-        let buffer = buffer_handle.read(cx);
-        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
+        let range = {
+            let buffer = buffer_handle.read(cx);
+            buffer.anchor_before(range.start)..buffer.anchor_before(range.end)
+        };
         self.lsp_store.update(cx, |lsp_store, cx| {
             lsp_store.inlay_hints(buffer_handle, range, cx)
         })
@@ -4214,8 +4254,8 @@ impl Project {
     pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut Context<Self>) {
         let new_active_entry = entry.and_then(|project_path| {
             let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
-            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
-            Some(entry.id)
+            let entry_id = worktree.read(cx).entry_for_path(project_path.path)?.id;
+            Some(entry_id)
         });
         if new_active_entry != self.active_entry {
             self.active_entry = new_active_entry;
@@ -4226,13 +4266,20 @@ impl Project {
         }
     }
 
+    // pub fn language_servers_running_disk_based_diagnostics<'a>(
+    //     &'a self,
+    //     cx: &'a App,
+    // ) -> impl Iterator<Item = LanguageServerId> + 'a {
+    //     self.lsp_store
+    //         .read(cx)
+    //         .language_servers_running_disk_based_diagnostics()
+    // }
     pub fn language_servers_running_disk_based_diagnostics<'a>(
         &'a self,
         cx: &'a App,
     ) -> impl Iterator<Item = LanguageServerId> + 'a {
-        self.lsp_store
-            .read(cx)
-            .language_servers_running_disk_based_diagnostics()
+        // todo!("language_servers_running_disk_based_diagnostics needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn diagnostic_summary(&self, include_ignored: bool, cx: &App) -> DiagnosticSummary {
@@ -4241,14 +4288,22 @@ impl Project {
             .diagnostic_summary(include_ignored, cx)
     }
 
+    // pub fn diagnostic_summaries<'a>(
+    //     &'a self,
+    //     include_ignored: bool,
+    //     cx: &'a App,
+    // ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
+    //     self.lsp_store
+    //         .read(cx)
+    //         .diagnostic_summaries(include_ignored, cx)
+    // }
     pub fn diagnostic_summaries<'a>(
         &'a self,
         include_ignored: bool,
         cx: &'a App,
     ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
-        self.lsp_store
-            .read(cx)
-            .diagnostic_summaries(include_ignored, cx)
+        // todo!("diagnostic_summaries needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn active_entry(&self) -> Option<ProjectEntryId> {
@@ -4307,25 +4362,26 @@ impl Project {
                 }
             }
         } else {
-            for worktree in worktree_store.visible_worktrees(cx) {
-                let worktree_root_name = worktree.read(cx).root_name();
-                if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
-                    return Some(ProjectPath {
-                        worktree_id: worktree.read(cx).id(),
-                        path: relative_path.into(),
-                    });
-                }
-            }
-
-            for worktree in worktree_store.visible_worktrees(cx) {
-                let worktree = worktree.read(cx);
-                if let Some(entry) = worktree.entry_for_path(path) {
-                    return Some(ProjectPath {
-                        worktree_id: worktree.id(),
-                        path: entry.path.clone(),
-                    });
-                }
-            }
+            // TODO: Fix when visible_worktrees is refactored to handle Ref type
+            // for worktree in worktree_store.visible_worktrees(cx) {
+            //     let worktree_root_name = worktree.read(cx).root_name();
+            //     if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
+            //         return Some(ProjectPath {
+            //             worktree_id: worktree.read(cx).id(),
+            //             path: relative_path.into(),
+            //         });
+            //     }
+            // }
+
+            // for worktree in worktree_store.visible_worktrees(cx) {
+            //     let worktree = worktree.read(cx);
+            //     if let Some(entry) = worktree.entry_for_path(path) {
+            //         return Some(ProjectPath {
+            //             worktree_id: worktree.id(),
+            //             path: entry.path.clone(),
+            //         });
+            //     }
+            // }
         }
 
         None
@@ -4868,16 +4924,21 @@ impl Project {
         self.worktree_store.read(cx).worktree_metadata_protos(cx)
     }
 
+    // /// Iterator of all open buffers that have unsaved changes
+    // pub fn dirty_buffers<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = ProjectPath> + 'a {
+    //     self.buffer_store.read(cx).buffers().filter_map(|buf| {
+    //         let buf = buf.read(cx);
+    //         if buf.is_dirty() {
+    //             buf.project_path(cx)
+    //         } else {
+    //             None
+    //         }
+    //     })
+    // }
     /// Iterator of all open buffers that have unsaved changes
     pub fn dirty_buffers<'a>(&'a self, cx: &'a App) -> impl Iterator<Item = ProjectPath> + 'a {
-        self.buffer_store.read(cx).buffers().filter_map(|buf| {
-            let buf = buf.read(cx);
-            if buf.is_dirty() {
-                buf.project_path(cx)
-            } else {
-                None
-            }
-        })
+        // todo!("dirty_buffers needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     fn set_worktrees_from_proto(
@@ -4909,11 +4970,18 @@ impl Project {
         Ok(())
     }
 
+    // pub fn supplementary_language_servers<'a>(
+    //     &'a self,
+    //     cx: &'a App,
+    // ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
+    //     self.lsp_store.read(cx).supplementary_language_servers()
+    // }
     pub fn supplementary_language_servers<'a>(
         &'a self,
         cx: &'a App,
     ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
-        self.lsp_store.read(cx).supplementary_language_servers()
+        // todo!("supplementary_language_servers needs to be refactored to handle Ref type")
+        std::iter::empty()
     }
 
     pub fn any_language_server_supports_inlay_hints(&self, buffer: &Buffer, cx: &mut App) -> bool {
@@ -5031,8 +5099,16 @@ impl Project {
         self.git_store.read(cx).active_repository()
     }
 
-    pub fn repositories<'a>(&self, cx: &'a App) -> &'a HashMap<RepositoryId, Entity<Repository>> {
-        self.git_store.read(cx).repositories()
+    // pub fn repositories<'a>(&self, cx: &'a App) -> &'a HashMap<RepositoryId, Entity<Repository>> {
+    //     self.git_store.read(cx).repositories()
+    // }
+    pub fn repositories<'a>(&self, _cx: &'a App) -> &'a HashMap<RepositoryId, Entity<Repository>> {
+        // todo!("repositories needs to be refactored to handle Ref type")
+        // This can't return an empty iterator since it needs to return a reference
+        // For now, we'll leak a static empty HashMap
+        static EMPTY: std::sync::OnceLock<HashMap<RepositoryId, Entity<Repository>>> =
+            std::sync::OnceLock::new();
+        EMPTY.get_or_init(HashMap::default)
     }
 
     pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {

crates/project/src/project_tests.rs 🔗

@@ -117,7 +117,8 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) {
     .await;
 
     project.update(cx, |project, cx| {
-        let tree = project.worktrees(cx).next().unwrap().read(cx);
+        let worktree = project.worktrees(cx).next().unwrap();
+        let tree = worktree.read(cx);
         assert_eq!(tree.file_count(), 5);
         assert_eq!(
             tree.inode_for_path("fennel/grape"),
@@ -1075,10 +1076,10 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
 
     // Initially, we don't load ignored files because the language server has not explicitly asked us to watch them.
     project.update(cx, |project, cx| {
-        let worktree = project.worktrees(cx).next().unwrap();
+        let worktree_entity = project.worktrees(cx).next().unwrap();
+        let worktree = worktree_entity.read(cx);
         assert_eq!(
             worktree
-                .read(cx)
                 .snapshot()
                 .entries(true, 0)
                 .map(|entry| (entry.path.as_ref(), entry.is_ignored))
@@ -3014,7 +3015,10 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
                 .abs_path(cx),
             Path::new(path!("/dir/a.rs")),
         );
-        assert_eq!(definition.target.range.to_offset(target_buffer), 9..10);
+        assert_eq!(
+            definition.target.range.to_offset(&target_buffer.snapshot()),
+            9..10
+        );
         assert_eq!(
             list_worktrees(&project, cx),
             [
@@ -3023,6 +3027,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
             ],
         );
 
+        drop(target_buffer);
         drop(definition);
     });
     cx.update(|cx| {
@@ -3032,18 +3037,19 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
         );
     });
 
-    fn list_worktrees<'a>(project: &'a Entity<Project>, cx: &'a App) -> Vec<(&'a Path, bool)> {
-        project
-            .read(cx)
-            .worktrees(cx)
-            .map(|worktree| {
-                let worktree = worktree.read(cx);
-                (
-                    worktree.as_local().unwrap().abs_path().as_ref(),
-                    worktree.is_visible(),
-                )
-            })
-            .collect::<Vec<_>>()
+    // fn list_worktrees<'a>(project: &'a Entity<Project>, cx: &'a App) -> Vec<(&'a Path, bool)> {
+    //     project
+    //         .read(cx)
+    //         .worktrees(cx)
+    //         .map(|worktree| {
+    //             let worktree = worktree.read(cx);
+    //             (
+    //                 worktree.as_local().unwrap().abs_path().as_ref(),
+    //                 worktree.is_visible(),
+    //             )
+    //         })
+    fn list_worktrees<'a>(_project: &'a Entity<Project>, _cx: &'a App) -> Vec<(&'a Path, bool)> {
+        todo!("list_worktrees needs to be refactored to handle Ref type")
     }
 }
 
@@ -4823,8 +4829,8 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) {
     let fake_server = fake_servers.next().await.unwrap();
     let response = project.update(cx, |project, cx| {
         let worktree = project.worktrees(cx).next().unwrap();
-        let entry = worktree.read(cx).entry_for_path("one.rs").unwrap();
-        project.rename_entry(entry.id, "three.rs".as_ref(), cx)
+        let entry_id = worktree.read(cx).entry_for_path("one.rs").unwrap().id;
+        project.rename_entry(entry_id, "three.rs".as_ref(), cx)
     });
     let expected_edit = lsp::WorkspaceEdit {
         changes: None,

crates/project/src/task_inventory.rs 🔗

@@ -305,12 +305,14 @@ impl Inventory {
         let last_scheduled_scenarios = self.last_scheduled_scenarios.iter().cloned().collect();
 
         let adapter = task_contexts.location().and_then(|location| {
-            let (file, language) = {
+            let (file, language_name, language) = {
                 let buffer = location.buffer.read(cx);
-                (buffer.file(), buffer.language())
+                let file = buffer.file().cloned();
+                let language = buffer.language().clone();
+                let language_name = language.as_ref().map(|l| l.name());
+                (file, language_name, language)
             };
-            let language_name = language.as_ref().map(|l| l.name());
-            let adapter = language_settings(language_name, file, cx)
+            let adapter = language_settings(language_name, file.as_ref(), cx)
                 .debuggers
                 .first()
                 .map(SharedString::from)
@@ -435,11 +437,17 @@ 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().clone());
+        let language = location.and_then(|location| {
+            let buffer = location.buffer.read(cx);
+            buffer.language().clone()
+        });
         let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
             name: language.name().into(),
         });
-        let file = location.and_then(|location| location.buffer.read(cx).file().cloned());
+        let file = location.and_then(|location| {
+            let buffer = location.buffer.read(cx);
+            buffer.file().cloned()
+        });
 
         let mut task_labels_to_ids = HashMap::<String, HashSet<TaskId>>::default();
         let mut lru_score = 0_u32;

crates/project/src/terminals.rs 🔗

@@ -478,9 +478,12 @@ impl Project {
                 let bin_path = venv_path.join(bin_dir_name);
                 self.find_worktree(&bin_path, cx)
                     .and_then(|(worktree, relative_path)| {
-                        worktree.read(cx).entry_for_path(&relative_path)
+                        worktree
+                            .read(cx)
+                            .entry_for_path(&relative_path)
+                            .map(|entry| entry.is_dir())
                     })
-                    .is_some_and(|entry| entry.is_dir())
+                    .unwrap_or(false)
             })
     }
 
@@ -491,7 +494,7 @@ impl Project {
         cx: &App,
     ) -> Option<PathBuf> {
         let (worktree, _) = self.find_worktree(abs_path, cx)?;
-        let fs = worktree.read(cx).as_local()?.fs();
+        let fs = worktree.read(cx).as_local()?.fs().clone();
         let bin_dir_name = match std::env::consts::OS {
             "windows" => "Scripts",
             _ => "bin",

crates/project/src/toolchain_store.rs 🔗

@@ -338,15 +338,16 @@ impl LocalToolchainStore {
                 .ok()?;
             let toolchains = language.toolchain_lister()?;
             let manifest_name = toolchains.manifest_name();
-            let (snapshot, worktree) = this
+            let worktree = this
                 .update(cx, |this, cx| {
-                    this.worktree_store
-                        .read(cx)
-                        .worktree_for_id(path.worktree_id, cx)
-                        .map(|worktree| (worktree.read(cx).snapshot(), worktree))
+                    let store = this.worktree_store.read(cx);
+                    store.worktree_for_id(path.worktree_id, cx)
                 })
                 .ok()
                 .flatten()?;
+            let snapshot = worktree
+                .read_with(cx, |worktree, _| worktree.snapshot())
+                .ok()?;
             let worktree_id = snapshot.id();
             let worktree_root = snapshot.abs_path().to_path_buf();
             let relative_path = manifest_tree

crates/project/src/worktree_store.rs 🔗

@@ -185,22 +185,36 @@ impl WorktreeStore {
         }
     }
 
-    pub fn entry_for_id<'a>(&'a self, entry_id: ProjectEntryId, cx: &'a App) -> Option<&'a Entry> {
-        self.worktrees()
-            .find_map(|worktree| worktree.read(cx).entry_for_id(entry_id))
-    }
-
+    // pub fn entry_for_id<'a>(&'a self, entry_id: ProjectEntryId, cx: &'a App) -> Option<&'a Entry> {
+    //     self.worktrees()
+    //         .find_map(|worktree| worktree.read(cx).entry_for_id(entry_id))
+    // }
+    pub fn entry_for_id<'a>(
+        &'a self,
+        _entry_id: ProjectEntryId,
+        _cx: &'a App,
+    ) -> Option<&'a Entry> {
+        todo!("entry_for_id needs to be refactored to handle Ref type")
+    }
+
+    // pub fn worktree_and_entry_for_id<'a>(
+    //     &'a self,
+    //     entry_id: ProjectEntryId,
+    //     cx: &'a App,
+    // ) -> Option<(Entity<Worktree>, &'a Entry)> {
+    //     self.worktrees().find_map(|worktree| {
+    //         worktree
+    //             .read(cx)
+    //             .entry_for_id(entry_id)
+    //             .map(|e| (worktree.clone(), e))
+    //     })
+    // }
     pub fn worktree_and_entry_for_id<'a>(
         &'a self,
-        entry_id: ProjectEntryId,
-        cx: &'a App,
+        _entry_id: ProjectEntryId,
+        _cx: &'a App,
     ) -> Option<(Entity<Worktree>, &'a Entry)> {
-        self.worktrees().find_map(|worktree| {
-            worktree
-                .read(cx)
-                .entry_for_id(entry_id)
-                .map(|e| (worktree.clone(), e))
-        })
+        todo!("worktree_and_entry_for_id needs to be refactored to handle Ref type")
     }
 
     pub fn entry_for_path(&self, path: &ProjectPath, cx: &App) -> Option<Entry> {
@@ -453,7 +467,8 @@ impl WorktreeStore {
             .drain(..)
             .filter_map(|worktree| {
                 let worktree = worktree.upgrade()?;
-                Some((worktree.read(cx).id(), worktree))
+                let worktree_id = worktree.read(cx).id();
+                Some((worktree_id, worktree))
             })
             .collect::<HashMap<_, _>>();