ep_cli: Fix "Too many open files" errors (#47243)

Ben Kunkle created

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/collab/src/tests/test_server.rs                    |  5 +
crates/component_preview/src/component_preview_example.rs |  5 +
crates/edit_prediction_cli/src/load_project.rs            | 15 +++
crates/edit_prediction_cli/src/main.rs                    | 28 ++++++--
crates/eval/src/instance.rs                               |  5 +
crates/inspector_ui/src/inspector.rs                      |  5 +
crates/project/src/lsp_store.rs                           | 11 ++
crates/project/src/project.rs                             | 30 +++++++-
crates/project/src/project_settings.rs                    | 29 +++++---
crates/project_benchmarks/src/main.rs                     |  5 +
crates/recent_projects/src/remote_connections.rs          |  5 +
crates/remote_server/src/headless_project.rs              |  1 
crates/workspace/src/workspace.rs                         |  2 
crates/zed/src/visual_test_runner.rs                      | 25 ++++++-
crates/zed/src/zed/visual_tests.rs                        |  5 +
15 files changed, 135 insertions(+), 41 deletions(-)

Detailed changes

crates/collab/src/tests/test_server.rs 🔗

@@ -862,7 +862,10 @@ impl TestClient {
                 self.app_state.languages.clone(),
                 self.app_state.fs.clone(),
                 None,
-                init_worktree_trust,
+                project::LocalProjectFlags {
+                    init_worktree_trust,
+                    ..Default::default()
+                },
                 cx,
             )
         })

crates/edit_prediction_cli/src/load_project.rs 🔗

@@ -211,7 +211,10 @@ async fn setup_project(
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                watch_global_configs: false,
+            },
             cx,
         )
     });
@@ -229,10 +232,16 @@ async fn setup_project(
 
     let buffer_store = project.read_with(cx, |project, _| project.buffer_store().clone());
     cx.subscribe(&buffer_store, {
-        let project = project.clone();
+        let project = project.downgrade();
+        let ep_store = ep_store.downgrade();
         move |_, event, cx| match event {
             BufferStoreEvent::BufferAdded(buffer) => {
-                ep_store.update(cx, |store, cx| store.register_buffer(&buffer, &project, cx));
+                let Some(project) = project.upgrade() else {
+                    return;
+                };
+                ep_store
+                    .update(cx, |store, cx| store.register_buffer(&buffer, &project, cx))
+                    .ok();
             }
             _ => {}
         }

crates/edit_prediction_cli/src/main.rs 🔗

@@ -22,7 +22,7 @@ use collections::HashSet;
 use edit_prediction::EditPredictionStore;
 use futures::channel::mpsc;
 use futures::{SinkExt as _, StreamExt as _};
-use gpui::{AppContext as _, Application, BackgroundExecutor};
+use gpui::{AppContext as _, Application, BackgroundExecutor, Task};
 use zeta_prompt::ZetaVersion;
 
 use reqwest_client::ReqwestClient;
@@ -674,23 +674,35 @@ fn main() {
                                 }
                             }
 
-                            if let Some(state) =
-                                repo_examples.first().and_then(|e| e.state.as_ref())
-                            {
+                            let repo_url = &repo_examples.first().unwrap().spec.repository_url;
+                            let project = repo_examples
+                                .iter()
+                                .find_map(|e| e.state.as_ref().map(|s| s.project.clone()))
+                                .or_else(|| app_state.project_cache.get(repo_url));
+
+                            if let Some(project) = project {
                                 let mut cx = cx.clone();
+
+                                let shutdown_task: Task<()> =
+                                    project.update(&mut cx, |project, cx| {
+                                        let lsp_store = project.lsp_store();
+                                        lsp_store.update(cx, |lsp_store, cx| {
+                                            lsp_store.shutdown_all_language_servers(cx)
+                                        })
+                                    });
+
+                                shutdown_task.await;
+
                                 if let Some(ep_store) =
                                     cx.update(|cx| EditPredictionStore::try_global(cx))
                                 {
-                                    let project = state.project.clone();
                                     ep_store.update(&mut cx, |store, _| {
                                         store.remove_project(&project);
                                     });
                                 }
                             }
 
-                            app_state
-                                .project_cache
-                                .remove(&repo_examples.first().unwrap().spec.repository_url);
+                            app_state.project_cache.remove(repo_url);
                             for example in &mut repo_examples {
                                 example.state.take();
                             }

crates/eval/src/instance.rs 🔗

@@ -202,7 +202,10 @@ impl ExampleInstance {
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         );
 

crates/inspector_ui/src/inspector.rs 🔗

@@ -33,7 +33,10 @@ pub fn init(app_state: Arc<AppState>, cx: &mut App) {
         app_state.languages.clone(),
         app_state.fs.clone(),
         None,
-        false,
+        project::LocalProjectFlags {
+            init_worktree_trust: false,
+            ..Default::default()
+        },
         cx,
     );
 

crates/project/src/lsp_store.rs 🔗

@@ -11036,6 +11036,10 @@ impl LspStore {
     }
 
     pub fn stop_all_language_servers(&mut self, cx: &mut Context<Self>) {
+        self.shutdown_all_language_servers(cx).detach();
+    }
+
+    pub fn shutdown_all_language_servers(&mut self, cx: &mut Context<Self>) -> Task<()> {
         if let Some((client, project_id)) = self.upstream_client() {
             let request = client.request(proto::StopLanguageServers {
                 project_id,
@@ -11043,10 +11047,12 @@ impl LspStore {
                 also_servers: Vec::new(),
                 all: true,
             });
-            cx.background_spawn(request).detach_and_log_err(cx);
+            cx.background_spawn(async move {
+                request.await.ok();
+            })
         } else {
             let Some(local) = self.as_local_mut() else {
-                return;
+                return Task::ready(());
             };
             let language_servers_to_stop = local
                 .language_server_ids
@@ -11061,7 +11067,6 @@ impl LspStore {
             cx.background_spawn(async move {
                 futures::future::join_all(tasks).await;
             })
-            .detach();
         }
     }
 

crates/project/src/project.rs 🔗

@@ -164,6 +164,21 @@ pub use lsp_store::{
 pub use toolchain_store::{ToolchainStore, Toolchains};
 const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
 
+#[derive(Clone, Copy, Debug)]
+pub struct LocalProjectFlags {
+    pub init_worktree_trust: bool,
+    pub watch_global_configs: bool,
+}
+
+impl Default for LocalProjectFlags {
+    fn default() -> Self {
+        Self {
+            init_worktree_trust: true,
+            watch_global_configs: true,
+        }
+    }
+}
+
 pub trait ProjectItem: 'static {
     fn try_open(
         project: &Entity<Project>,
@@ -1088,7 +1103,7 @@ impl Project {
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
         env: Option<HashMap<String, String>>,
-        init_worktree_trust: bool,
+        flags: LocalProjectFlags,
         cx: &mut App,
     ) -> Entity<Self> {
         cx.new(|cx: &mut Context<Self>| {
@@ -1097,7 +1112,7 @@ impl Project {
                 .detach();
             let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
             let worktree_store = cx.new(|_| WorktreeStore::local(false, fs.clone()));
-            if init_worktree_trust {
+            if flags.init_worktree_trust {
                 trusted_worktrees::track_worktree_trust(
                     worktree_store.clone(),
                     None,
@@ -1185,6 +1200,7 @@ impl Project {
                     fs.clone(),
                     worktree_store.clone(),
                     task_store.clone(),
+                    flags.watch_global_configs,
                     cx,
                 )
             });
@@ -1899,7 +1915,10 @@ impl Project {
                 Arc::new(languages),
                 fs,
                 None,
-                false,
+                LocalProjectFlags {
+                    init_worktree_trust: false,
+                    ..Default::default()
+                },
                 cx,
             )
         });
@@ -1956,7 +1975,10 @@ impl Project {
                 Arc::new(languages),
                 fs,
                 None,
-                init_worktree_trust,
+                LocalProjectFlags {
+                    init_worktree_trust,
+                    ..Default::default()
+                },
                 cx,
             )
         });

crates/project/src/project_settings.rs 🔗

@@ -691,6 +691,7 @@ impl SettingsObserver {
         fs: Arc<dyn Fs>,
         worktree_store: Entity<WorktreeStore>,
         task_store: Entity<TaskStore>,
+        watch_global_configs: bool,
         cx: &mut Context<Self>,
     ) -> Self {
         cx.subscribe(&worktree_store, Self::on_worktree_store_event)
@@ -787,16 +788,24 @@ impl SettingsObserver {
             _user_settings_watcher: None,
             _editorconfig_watcher: Some(_editorconfig_watcher),
             project_id: REMOTE_SERVER_PROJECT_ID,
-            _global_task_config_watcher: Self::subscribe_to_global_task_file_changes(
-                fs.clone(),
-                paths::tasks_file().clone(),
-                cx,
-            ),
-            _global_debug_config_watcher: Self::subscribe_to_global_debug_scenarios_changes(
-                fs.clone(),
-                paths::debug_scenarios_file().clone(),
-                cx,
-            ),
+            _global_task_config_watcher: if watch_global_configs {
+                Self::subscribe_to_global_task_file_changes(
+                    fs.clone(),
+                    paths::tasks_file().clone(),
+                    cx,
+                )
+            } else {
+                Task::ready(())
+            },
+            _global_debug_config_watcher: if watch_global_configs {
+                Self::subscribe_to_global_debug_scenarios_changes(
+                    fs.clone(),
+                    paths::debug_scenarios_file().clone(),
+                    cx,
+                )
+            } else {
+                Task::ready(())
+            },
         }
     }
 

crates/project_benchmarks/src/main.rs 🔗

@@ -160,7 +160,10 @@ fn main() -> Result<(), anyhow::Error> {
                     registry,
                     fs,
                     Some(Default::default()),
-                    false,
+                    project::LocalProjectFlags {
+                        init_worktree_trust: false,
+                        ..Default::default()
+                    },
                     cx,
                 ))
                 };

crates/recent_projects/src/remote_connections.rs 🔗

@@ -659,7 +659,10 @@ pub async fn open_remote_project(
                 app_state.languages.clone(),
                 app_state.fs.clone(),
                 None,
-                false,
+                project::LocalProjectFlags {
+                    init_worktree_trust: false,
+                    ..Default::default()
+                },
                 cx,
             );
             cx.new(|cx| {

crates/zed/src/visual_test_runner.rs 🔗

@@ -222,7 +222,10 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });
@@ -900,7 +903,10 @@ fn run_breakpoint_hover_visual_tests(
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });
@@ -1212,7 +1218,10 @@ import { AiPaneTabContext } from 'context';
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });
@@ -1696,7 +1705,10 @@ fn run_subagent_visual_tests(
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });
@@ -2018,7 +2030,10 @@ fn run_agent_thread_view_test(
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });

crates/zed/src/zed/visual_tests.rs 🔗

@@ -82,7 +82,10 @@ pub async fn open_test_workspace(
             app_state.languages.clone(),
             app_state.fs.clone(),
             None,
-            false,
+            project::LocalProjectFlags {
+                init_worktree_trust: false,
+                ..Default::default()
+            },
             cx,
         )
     });