Get remote use case working

Anthony Eid created

Change summary

crates/project/src/lsp_store.rs              |  3 ++-
crates/project/src/project.rs                |  4 ++++
crates/project/src/worktree_store.rs         | 11 ++++++++++-
crates/proto/proto/worktree.proto            |  2 ++
crates/remote_server/src/headless_project.rs |  3 +++
crates/sidebar/src/sidebar_tests.rs          | 16 ----------------
crates/workspace/src/multi_workspace.rs      | 17 +++++++++++++++++
crates/worktree/src/worktree.rs              |  9 ++++++++-
8 files changed, 46 insertions(+), 19 deletions(-)

Detailed changes

crates/project/src/lsp_store.rs 🔗

@@ -4430,7 +4430,8 @@ impl LspStore {
             WorktreeStoreEvent::WorktreeReleased(..)
             | WorktreeStoreEvent::WorktreeOrderChanged
             | WorktreeStoreEvent::WorktreeUpdatedGitRepositories(..)
-            | WorktreeStoreEvent::WorktreeDeletedEntry(..) => {}
+            | WorktreeStoreEvent::WorktreeDeletedEntry(..)
+            | WorktreeStoreEvent::WorktreeUpdatedRootRepoCommonDir(..) => {}
         }
     }
 

crates/project/src/project.rs 🔗

@@ -359,6 +359,7 @@ pub enum Event {
     WorktreeOrderChanged,
     WorktreeRemoved(WorktreeId),
     WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
+    WorktreeUpdatedRootRepoCommonDir(WorktreeId),
     DiskBasedDiagnosticsStarted {
         language_server_id: LanguageServerId,
     },
@@ -3680,6 +3681,9 @@ impl Project {
             }
             // Listen to the GitStore instead.
             WorktreeStoreEvent::WorktreeUpdatedGitRepositories(_, _) => {}
+            WorktreeStoreEvent::WorktreeUpdatedRootRepoCommonDir(worktree_id) => {
+                cx.emit(Event::WorktreeUpdatedRootRepoCommonDir(*worktree_id));
+            }
         }
     }
 

crates/project/src/worktree_store.rs 🔗

@@ -91,6 +91,7 @@ pub enum WorktreeStoreEvent {
     WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
     WorktreeUpdatedGitRepositories(WorktreeId, UpdatedGitRepositoriesSet),
     WorktreeDeletedEntry(WorktreeId, ProjectEntryId),
+    WorktreeUpdatedRootRepoCommonDir(WorktreeId),
 }
 
 impl EventEmitter<WorktreeStoreEvent> for WorktreeStore {}
@@ -712,6 +713,7 @@ impl WorktreeStore {
                         root_name,
                         visible,
                         abs_path: response.canonicalized_path,
+                        root_repo_common_dir: response.root_repo_common_dir,
                     },
                     client,
                     path_style,
@@ -812,7 +814,11 @@ impl WorktreeStore {
                     // The worktree root itself has been deleted (for single-file worktrees)
                     // The worktree will be removed via the observe_release callback
                 }
-                worktree::Event::UpdatedRootRepoCommonDir => {}
+                worktree::Event::UpdatedRootRepoCommonDir => {
+                    cx.emit(WorktreeStoreEvent::WorktreeUpdatedRootRepoCommonDir(
+                        worktree_id,
+                    ));
+                }
             }
         })
         .detach();
@@ -1049,6 +1055,9 @@ impl WorktreeStore {
                     root_name: worktree.root_name_str().to_owned(),
                     visible: worktree.is_visible(),
                     abs_path: worktree.abs_path().to_string_lossy().into_owned(),
+                    root_repo_common_dir: worktree
+                        .root_repo_common_dir()
+                        .map(|p| p.to_string_lossy().into_owned()),
                 }
             })
             .collect()

crates/proto/proto/worktree.proto 🔗

@@ -40,6 +40,7 @@ message AddWorktree {
 message AddWorktreeResponse {
   uint64 worktree_id = 1;
   string canonicalized_path = 2;
+  optional string root_repo_common_dir = 3;
 }
 
 message RemoveWorktree {
@@ -62,6 +63,7 @@ message WorktreeMetadata {
   string root_name = 2;
   bool visible = 3;
   string abs_path = 4;
+  optional string root_repo_common_dir = 5;
 }
 
 message ProjectPath {

crates/remote_server/src/headless_project.rs 🔗

@@ -523,6 +523,9 @@ impl HeadlessProject {
             proto::AddWorktreeResponse {
                 worktree_id: worktree.id().to_proto(),
                 canonicalized_path: canonicalized.to_string_lossy().into_owned(),
+                root_repo_common_dir: worktree
+                    .root_repo_common_dir()
+                    .map(|p| p.to_string_lossy().into_owned()),
             }
         });
 

crates/sidebar/src/sidebar_tests.rs 🔗

@@ -5901,22 +5901,6 @@ async fn test_clicking_closed_remote_thread_opens_remote_workspace(
         mw.add_project_group_key(stale_key);
     });
 
-    // Also save a thread whose main_worktree_paths uses the stale
-    // path. This simulates a thread created while the workspace's
-    // project_group_key was still using the fallback abs_path.
-    cx.update(|_window, cx| {
-        let metadata = ThreadMetadata {
-            session_id: acp::SessionId::new(Arc::from("stale-thread")),
-            agent_id: agent::ZED_AGENT_ID.clone(),
-            title: "Stale Thread".into(),
-            updated_at: chrono::TimeZone::with_ymd_and_hms(&Utc, 2024, 1, 1, 0, 0, 2).unwrap(),
-            created_at: None,
-            folder_paths: PathList::new(&[PathBuf::from("/project-wt-1")]),
-            main_worktree_paths: PathList::new(&[PathBuf::from("/project-wt-1")]),
-            archived: false,
-        };
-        ThreadMetadataStore::global(cx).update(cx, |store, cx| store.save_manually(metadata, cx));
-    });
     cx.run_until_parked();
 
     // After adding the linked worktree workspace, the sidebar should

crates/workspace/src/multi_workspace.rs 🔗

@@ -582,6 +582,13 @@ impl MultiWorkspace {
                         this.add_project_group_key(workspace.read(cx).project_group_key(cx));
                     }
                 }
+                project::Event::WorktreeUpdatedRootRepoCommonDir(_) => {
+                    if let Some(workspace) = workspace.upgrade() {
+                        this.add_project_group_key(workspace.read(cx).project_group_key(cx));
+                        this.remove_stale_project_group_keys(cx);
+                        cx.notify();
+                    }
+                }
                 _ => {}
             }
         })
@@ -605,6 +612,16 @@ impl MultiWorkspace {
         self.project_group_keys.push(project_group_key);
     }
 
+    fn remove_stale_project_group_keys(&mut self, cx: &App) {
+        let workspace_keys: std::collections::HashSet<ProjectGroupKey> = self
+            .workspaces
+            .iter()
+            .map(|ws| ws.read(cx).project_group_key(cx))
+            .collect();
+        self.project_group_keys
+            .retain(|key| workspace_keys.contains(key));
+    }
+
     pub fn restore_project_group_keys(&mut self, keys: Vec<ProjectGroupKey>) {
         let mut restored = keys;
         for existing_key in &self.project_group_keys {

crates/worktree/src/worktree.rs 🔗

@@ -510,7 +510,7 @@ impl Worktree {
         cx: &mut App,
     ) -> Entity<Self> {
         cx.new(|cx: &mut Context<Self>| {
-            let snapshot = Snapshot::new(
+            let mut snapshot = Snapshot::new(
                 WorktreeId::from_proto(worktree.id),
                 RelPath::from_proto(&worktree.root_name)
                     .unwrap_or_else(|_| RelPath::empty().into()),
@@ -518,6 +518,10 @@ impl Worktree {
                 path_style,
             );
 
+            snapshot.root_repo_common_dir = worktree
+                .root_repo_common_dir
+                .map(|p| SanitizedPath::new_arc(Path::new(&p)));
+
             let background_snapshot = Arc::new(Mutex::new((
                 snapshot.clone(),
                 Vec::<proto::UpdateWorktree>::new(),
@@ -676,6 +680,9 @@ impl Worktree {
             root_name: self.root_name().to_proto(),
             visible: self.is_visible(),
             abs_path: self.abs_path().to_string_lossy().into_owned(),
+            root_repo_common_dir: self
+                .root_repo_common_dir()
+                .map(|p| p.to_string_lossy().into_owned()),
         }
     }