Limit language server reinstallation attempts

Julia created

zed2 electric boogaloo

Change summary

crates/language2/src/language2.rs |  4 ++
crates/project2/src/project2.rs   | 48 ++++++++++++++++++++------------
2 files changed, 33 insertions(+), 19 deletions(-)

Detailed changes

crates/language2/src/language2.rs 🔗

@@ -37,7 +37,7 @@ use std::{
     path::{Path, PathBuf},
     str,
     sync::{
-        atomic::{AtomicUsize, Ordering::SeqCst},
+        atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
         Arc,
     },
 };
@@ -115,6 +115,7 @@ pub struct CachedLspAdapter {
     pub disk_based_diagnostics_progress_token: Option<String>,
     pub language_ids: HashMap<String, String>,
     pub adapter: Arc<dyn LspAdapter>,
+    pub reinstall_attempt_count: AtomicU64,
 }
 
 impl CachedLspAdapter {
@@ -135,6 +136,7 @@ impl CachedLspAdapter {
             disk_based_diagnostics_progress_token,
             language_ids,
             adapter,
+            reinstall_attempt_count: AtomicU64::new(0),
         })
     }
 

crates/project2/src/project2.rs 🔗

@@ -92,6 +92,8 @@ use util::{
 pub use fs::*;
 pub use worktree::*;
 
+const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
+
 pub trait Item {
     fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
@@ -2774,6 +2776,10 @@ impl Project {
         language: Arc<Language>,
         cx: &mut ModelContext<Self>,
     ) {
+        if adapter.reinstall_attempt_count.load(SeqCst) > MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
+            return;
+        }
+
         let key = (worktree_id, adapter.name.clone());
         if self.language_server_ids.contains_key(&key) {
             return;
@@ -2833,28 +2839,34 @@ impl Project {
                     }
 
                     Err(err) => {
-                        log::error!("failed to start language server {:?}: {}", server_name, err);
+                        log::error!("failed to start language server {server_name:?}: {err}");
                         log::error!("server stderr: {:?}", stderr_capture.lock().take());
 
-                        if let Some(this) = this.upgrade() {
-                            if let Some(container_dir) = container_dir {
-                                let installation_test_binary = adapter
-                                    .installation_test_binary(container_dir.to_path_buf())
-                                    .await;
-
-                                this.update(&mut cx, |_, cx| {
-                                    Self::check_errored_server(
-                                        language,
-                                        adapter,
-                                        server_id,
-                                        installation_test_binary,
-                                        cx,
-                                    )
-                                })
-                                .ok();
-                            }
+                        let this = this.upgrade()?;
+                        let container_dir = container_dir?;
+
+                        let attempt_count = adapter.reinstall_attempt_count.fetch_add(1, SeqCst);
+                        if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
+                            let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
+                            log::error!("Hit {max} reinstallation attempts for {server_name:?}");
+                            return None;
                         }
 
+                        let installation_test_binary = adapter
+                            .installation_test_binary(container_dir.to_path_buf())
+                            .await;
+
+                        this.update(&mut cx, |_, cx| {
+                            Self::check_errored_server(
+                                language,
+                                adapter,
+                                server_id,
+                                installation_test_binary,
+                                cx,
+                            )
+                        })
+                        .ok();
+
                         None
                     }
                 }