Update new buffers with existing diagnostics in Project – after assigning language

Nathan Sobo created

Change summary

crates/project/src/project.rs  | 62 ++++++++++++++++++++++-------------
crates/project/src/worktree.rs | 32 ++++++++++--------
2 files changed, 56 insertions(+), 38 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -489,20 +489,20 @@ impl Project {
         path: impl Into<ProjectPath>,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
-        let path = path.into();
-        let worktree = if let Some(worktree) = self.worktree_for_id(path.worktree_id, cx) {
+        let project_path = path.into();
+        let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) {
             worktree
         } else {
             return Task::ready(Err(anyhow!("no such worktree")));
         };
 
         // If there is already a buffer for the given path, then return it.
-        let existing_buffer = self.get_open_buffer(&path, cx);
+        let existing_buffer = self.get_open_buffer(&project_path, cx);
         if let Some(existing_buffer) = existing_buffer {
             return Task::ready(Ok(existing_buffer));
         }
 
-        let mut loading_watch = match self.loading_buffers.entry(path.clone()) {
+        let mut loading_watch = match self.loading_buffers.entry(project_path.clone()) {
             // If the given path is already being loaded, then wait for that existing
             // task to complete and return the same buffer.
             hash_map::Entry::Occupied(e) => e.get().clone(),
@@ -512,16 +512,15 @@ impl Project {
                 let (mut tx, rx) = postage::watch::channel();
                 entry.insert(rx.clone());
 
-                let load_buffer = worktree.update(cx, |worktree, cx| match worktree {
-                    Worktree::Local(worktree) => worktree.open_buffer(&path.path, cx),
-                    Worktree::Remote(worktree) => worktree.open_buffer(&path.path, cx),
+                let load_buffer = worktree.update(cx, |worktree, cx| {
+                    worktree.load_buffer(&project_path.path, cx)
                 });
 
                 cx.spawn(move |this, mut cx| async move {
                     let load_result = load_buffer.await;
                     *tx.borrow_mut() = Some(this.update(&mut cx, |this, cx| {
                         // Record the fact that the buffer is no longer loading.
-                        this.loading_buffers.remove(&path);
+                        this.loading_buffers.remove(&project_path);
                         let buffer = load_result.map_err(Arc::new)?;
                         this.open_buffers.insert(
                             buffer.read(cx).remote_id() as usize,
@@ -623,31 +622,46 @@ impl Project {
         buffer: &ModelHandle<Buffer>,
         cx: &mut ModelContext<Self>,
     ) -> Option<()> {
+        let (path, full_path) = {
+            let file = buffer.read(cx).file()?;
+            (file.path().clone(), file.full_path())
+        };
+
         // Set the buffer's language
-        let full_path = buffer.read(cx).file()?.full_path();
         let language = self.languages.select_language(&full_path)?.clone();
         buffer.update(cx, |buffer, cx| {
             buffer.set_language(Some(language.clone()), cx);
         });
 
         // For local worktrees, start a language server if needed.
+        // Also assign the language server and any previously stored diagnostics to the buffer.
         let worktree = worktree.read(cx);
-        let worktree_id = worktree.id();
-        let worktree_abs_path = worktree.as_local()?.abs_path().clone();
-        let language_server = match self
-            .language_servers
-            .entry((worktree_id, language.name().to_string()))
-        {
-            hash_map::Entry::Occupied(e) => Some(e.get().clone()),
-            hash_map::Entry::Vacant(e) => {
-                Self::start_language_server(self.client.clone(), language, &worktree_abs_path, cx)
-                    .map(|server| e.insert(server).clone())
-            }
-        };
+        if let Some(local_worktree) = worktree.as_local() {
+            let worktree_id = local_worktree.id();
+            let diagnostics = local_worktree.diagnostics_for_path(&path);
+            let worktree_abs_path = local_worktree.abs_path().clone();
+
+            let language_server = match self
+                .language_servers
+                .entry((worktree_id, language.name().to_string()))
+            {
+                hash_map::Entry::Occupied(e) => Some(e.get().clone()),
+                hash_map::Entry::Vacant(e) => Self::start_language_server(
+                    self.client.clone(),
+                    language,
+                    &worktree_abs_path,
+                    cx,
+                )
+                .map(|server| e.insert(server).clone()),
+            };
 
-        buffer.update(cx, |buffer, cx| {
-            buffer.set_language_server(language_server, cx)
-        });
+            buffer.update(cx, |buffer, cx| {
+                buffer.set_language_server(language_server, cx);
+                if let Some(diagnostics) = diagnostics {
+                    buffer.update_diagnostics(None, diagnostics, cx).log_err();
+                }
+            });
+        }
 
         None
     }

crates/project/src/worktree.rs 🔗

@@ -347,6 +347,17 @@ impl Worktree {
         }
     }
 
+    pub fn load_buffer(
+        &mut self,
+        path: &Path,
+        cx: &mut ModelContext<Self>,
+    ) -> Task<Result<ModelHandle<Buffer>>> {
+        match self {
+            Worktree::Local(worktree) => worktree.load_buffer(path, cx),
+            Worktree::Remote(worktree) => worktree.load_buffer(path, cx),
+        }
+    }
+
     pub fn diagnostic_summaries<'a>(
         &'a self,
     ) -> impl Iterator<Item = (Arc<Path>, DiagnosticSummary)> + 'a {
@@ -536,7 +547,7 @@ impl LocalWorktree {
         self.config.collaborators.clone()
     }
 
-    pub(crate) fn open_buffer(
+    pub(crate) fn load_buffer(
         &mut self,
         path: &Path,
         cx: &mut ModelContext<Worktree>,
@@ -546,21 +557,14 @@ impl LocalWorktree {
             let (file, contents) = this
                 .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
                 .await?;
-
-            let diagnostics = this.update(&mut cx, |this, _| {
-                this.as_local_mut().unwrap().diagnostics.get(&path).cloned()
-            });
-
-            Ok(cx.add_model(|cx| {
-                let mut buffer = Buffer::from_file(0, contents, Box::new(file), cx);
-                if let Some(diagnostics) = diagnostics {
-                    buffer.update_diagnostics(None, diagnostics, cx).unwrap();
-                }
-                buffer
-            }))
+            Ok(cx.add_model(|cx| Buffer::from_file(0, contents, Box::new(file), cx)))
         })
     }
 
+    pub fn diagnostics_for_path(&self, path: &Path) -> Option<Vec<DiagnosticEntry<PointUtf16>>> {
+        self.diagnostics.get(path).cloned()
+    }
+
     pub fn update_diagnostics(
         &mut self,
         worktree_path: Arc<Path>,
@@ -798,7 +802,7 @@ impl LocalWorktree {
 }
 
 impl RemoteWorktree {
-    pub(crate) fn open_buffer(
+    pub(crate) fn load_buffer(
         &mut self,
         path: &Path,
         cx: &mut ModelContext<Worktree>,