]> git.lizzy.rs Git - rust.git/blobdiff - crates/rust-analyzer/src/main_loop.rs
fix: correctly update diagnostics when files are opened and closed
[rust.git] / crates / rust-analyzer / src / main_loop.rs
index 0518a17f3073885f31ce24d23dadd67a9a9b02c7..35fce79f5eb6a4dafaa1baf4e9c53d36e3bc7249 100644 (file)
@@ -406,25 +406,49 @@ fn handle_event(&mut self, event: Event) -> Result<()> {
         }
 
         let state_changed = self.process_changes();
+        let memdocs_added_or_removed = self.mem_docs.take_changes();
 
-        if self.is_quiescent() && !was_quiescent {
-            for flycheck in &self.flycheck {
-                flycheck.update();
+        if self.is_quiescent() {
+            if !was_quiescent {
+                for flycheck in &self.flycheck {
+                    flycheck.update();
+                }
             }
-        }
 
-        if self.is_quiescent() && (!was_quiescent || state_changed) {
-            self.update_file_notifications_on_threadpool();
+            if !was_quiescent || state_changed {
+                // Ensure that only one cache priming task can run at a time
+                self.prime_caches_queue.request_op();
+                if self.prime_caches_queue.should_start_op() {
+                    self.task_pool.handle.spawn_with_sender({
+                        let snap = self.snapshot();
+                        move |sender| {
+                            let cb = |progress| {
+                                sender.send(Task::PrimeCaches(progress)).unwrap();
+                            };
+                            match snap.analysis.prime_caches(cb) {
+                                Ok(()) => (),
+                                Err(_canceled) => (),
+                            }
+                        }
+                    });
+                }
 
-            // Refresh semantic tokens if the client supports it.
-            if self.config.semantic_tokens_refresh() {
-                self.semantic_tokens_cache.lock().clear();
-                self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ());
+                // Refresh semantic tokens if the client supports it.
+                if self.config.semantic_tokens_refresh() {
+                    self.semantic_tokens_cache.lock().clear();
+                    self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ());
+                }
+
+                // Refresh code lens if the client supports it.
+                if self.config.code_lens_refresh() {
+                    self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
+                }
             }
 
-            // Refresh code lens if the client supports it.
-            if self.config.code_lens_refresh() {
-                self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
+            if !was_quiescent || state_changed || memdocs_added_or_removed {
+                if self.config.publish_diagnostics() {
+                    self.update_diagnostics()
+                }
             }
         }
 
@@ -586,42 +610,32 @@ fn on_notification(&mut self, not: Notification) -> Result<()> {
                     {
                         log::error!("duplicate DidOpenTextDocument: {}", path)
                     }
-                    let changed = this
-                        .vfs
+                    this.vfs
                         .write()
                         .0
                         .set_file_contents(path, Some(params.text_document.text.into_bytes()));
-
-                    // If the VFS contents are unchanged, update diagnostics, since `handle_event`
-                    // won't see any changes. This avoids missing diagnostics when opening a file.
-                    //
-                    // If the file *was* changed, `handle_event` will already recompute and send
-                    // diagnostics. We can't do it here, since the *current* file contents might be
-                    // unset in salsa, since the VFS change hasn't been applied to the database yet.
-                    if !changed {
-                        this.maybe_update_diagnostics();
-                    }
                 }
                 Ok(())
             })?
             .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
-                    let doc = match this.mem_docs.get_mut(&path) {
-                        Some(doc) => doc,
+                    match this.mem_docs.get_mut(&path) {
+                        Some(doc) => {
+                            // The version passed in DidChangeTextDocument is the version after all edits are applied
+                            // so we should apply it before the vfs is notified.
+                            doc.version = params.text_document.version;
+                        }
                         None => {
                             log::error!("expected DidChangeTextDocument: {}", path);
                             return Ok(());
                         }
                     };
+
                     let vfs = &mut this.vfs.write().0;
                     let file_id = vfs.file_id(&path).unwrap();
                     let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
                     apply_document_changes(&mut text, params.content_changes);
 
-                    // The version passed in DidChangeTextDocument is the version after all edits are applied
-                    // so we should apply it before the vfs is notified.
-                    doc.version = params.text_document.version;
-
                     vfs.set_file_contents(path.clone(), Some(text.into_bytes()));
                 }
                 Ok(())
@@ -696,27 +710,8 @@ fn on_notification(&mut self, not: Notification) -> Result<()> {
             .finish();
         Ok(())
     }
-    fn update_file_notifications_on_threadpool(&mut self) {
-        self.maybe_update_diagnostics();
-
-        // Ensure that only one cache priming task can run at a time
-        self.prime_caches_queue.request_op();
-        if self.prime_caches_queue.should_start_op() {
-            self.task_pool.handle.spawn_with_sender({
-                let snap = self.snapshot();
-                move |sender| {
-                    let cb = |progress| {
-                        sender.send(Task::PrimeCaches(progress)).unwrap();
-                    };
-                    match snap.analysis.prime_caches(cb) {
-                        Ok(()) => (),
-                        Err(_canceled) => (),
-                    }
-                }
-            });
-        }
-    }
-    fn maybe_update_diagnostics(&mut self) {
+
+    fn update_diagnostics(&mut self) {
         let subscriptions = self
             .mem_docs
             .iter()
@@ -724,25 +719,24 @@ fn maybe_update_diagnostics(&mut self) {
             .collect::<Vec<_>>();
 
         log::trace!("updating notifications for {:?}", subscriptions);
-        if self.config.publish_diagnostics() {
-            let snapshot = self.snapshot();
-            self.task_pool.handle.spawn(move || {
-                let diagnostics = subscriptions
-                    .into_iter()
-                    .filter_map(|file_id| {
-                        handlers::publish_diagnostics(&snapshot, file_id)
-                            .map_err(|err| {
-                                if !is_cancelled(&*err) {
-                                    log::error!("failed to compute diagnostics: {:?}", err);
-                                }
-                                ()
-                            })
-                            .ok()
-                            .map(|diags| (file_id, diags))
-                    })
-                    .collect::<Vec<_>>();
-                Task::Diagnostics(diagnostics)
-            })
-        }
+
+        let snapshot = self.snapshot();
+        self.task_pool.handle.spawn(move || {
+            let diagnostics = subscriptions
+                .into_iter()
+                .filter_map(|file_id| {
+                    handlers::publish_diagnostics(&snapshot, file_id)
+                        .map_err(|err| {
+                            if !is_cancelled(&*err) {
+                                log::error!("failed to compute diagnostics: {:?}", err);
+                            }
+                            ()
+                        })
+                        .ok()
+                        .map(|diags| (file_id, diags))
+                })
+                .collect::<Vec<_>>();
+            Task::Diagnostics(diagnostics)
+        })
     }
 }