Make NodeRuntime non-static for prettier runner

Kirill Bulatov created

Change summary

Cargo.lock                        |  2 +
crates/collab/Cargo.toml          |  1 
crates/project/src/project.rs     | 37 ++++++++++++++++----------------
crates/workspace/Cargo.toml       |  1 
crates/workspace/src/workspace.rs |  8 +++++++
crates/zed/src/main.rs            | 10 +++++++-
6 files changed, 39 insertions(+), 20 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1501,6 +1501,7 @@ dependencies = [
  "log",
  "lsp",
  "nanoid",
+ "node_runtime",
  "parking_lot 0.11.2",
  "pretty_assertions",
  "project",
@@ -10003,6 +10004,7 @@ dependencies = [
  "lazy_static",
  "log",
  "menu",
+ "node_runtime",
  "parking_lot 0.11.2",
  "postage",
  "project",

crates/collab/Cargo.toml 🔗

@@ -72,6 +72,7 @@ fs = { path = "../fs", features = ["test-support"] }
 git = { path = "../git", features = ["test-support"] }
 live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
+node_runtime = { path = "../node_runtime" }
 project = { path = "../project", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/project/src/project.rs 🔗

@@ -72,7 +72,7 @@ use std::{
     str,
     sync::{
         atomic::{AtomicUsize, Ordering::SeqCst},
-        Arc, OnceLock,
+        Arc,
     },
     time::{Duration, Instant},
 };
@@ -154,6 +154,7 @@ pub struct Project {
     copilot_lsp_subscription: Option<gpui::Subscription>,
     copilot_log_subscription: Option<lsp::Subscription>,
     current_lsp_settings: HashMap<Arc<str>, LspSettings>,
+    node_runtime: Option<Arc<dyn NodeRuntime>>,
     prettier_instances: HashMap<
         (Option<WorktreeId>, PathBuf),
         Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>,
@@ -554,27 +555,14 @@ impl SearchMatchCandidate {
     }
 }
 
-static NODE_RUNTIME: OnceLock<Arc<dyn NodeRuntime>> = OnceLock::new();
-
 impl Project {
     pub fn init_settings(cx: &mut AppContext) {
         settings::register::<ProjectSettings>(cx);
     }
 
-    pub fn init(
-        client: &Arc<Client>,
-        node_runtime: Option<Arc<dyn NodeRuntime>>,
-        cx: &mut AppContext,
-    ) {
+    pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
         Self::init_settings(cx);
 
-        // TODO kb move it to Project::local and other constructors?
-        if let Some(node_runtime) = node_runtime {
-            NODE_RUNTIME
-                .set(node_runtime)
-                .unwrap_or_else(|_| panic!("multiple init calls tried to set node runtime"));
-        }
-
         client.add_model_message_handler(Self::handle_add_collaborator);
         client.add_model_message_handler(Self::handle_update_project_collaborator);
         client.add_model_message_handler(Self::handle_remove_collaborator);
@@ -624,6 +612,7 @@ impl Project {
 
     pub fn local(
         client: Arc<Client>,
+        node_runtime: Arc<dyn NodeRuntime>,
         user_store: ModelHandle<UserStore>,
         languages: Arc<LanguageRegistry>,
         fs: Arc<dyn Fs>,
@@ -679,6 +668,7 @@ impl Project {
                 copilot_lsp_subscription,
                 copilot_log_subscription: None,
                 current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
+                node_runtime: Some(node_runtime),
                 prettier_instances: HashMap::default(),
             }
         })
@@ -777,6 +767,7 @@ impl Project {
                 copilot_lsp_subscription,
                 copilot_log_subscription: None,
                 current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
+                node_runtime: None,
                 prettier_instances: HashMap::default(),
             };
             for worktree in worktrees {
@@ -811,13 +802,23 @@ impl Project {
         root_paths: impl IntoIterator<Item = &Path>,
         cx: &mut gpui::TestAppContext,
     ) -> ModelHandle<Project> {
+        use node_runtime::FakeNodeRuntime;
+
         let mut languages = LanguageRegistry::test();
         languages.set_executor(cx.background());
         let http_client = util::http::FakeHttpClient::with_404_response();
         let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
-        let project =
-            cx.update(|cx| Project::local(client, user_store, Arc::new(languages), fs, cx));
+        let project = cx.update(|cx| {
+            Project::local(
+                client,
+                FakeNodeRuntime::new(),
+                user_store,
+                Arc::new(languages),
+                fs,
+                cx,
+            )
+        });
         for path in root_paths {
             let (tree, _) = project
                 .update(cx, |project, cx| {
@@ -8201,7 +8202,7 @@ impl Project {
         buffer: &ModelHandle<Buffer>,
         cx: &mut ModelContext<Self>,
     ) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> {
-        let node_runtime = Arc::clone(NODE_RUNTIME.get()?);
+        let node_runtime = Arc::clone(self.node_runtime.as_ref()?);
         let buffer_file = File::from_dyn(buffer.read(cx).file());
         let buffer_path = buffer_file.map(|file| Arc::clone(file.path()));
         let worktree_path = buffer_file

crates/workspace/Cargo.toml 🔗

@@ -30,6 +30,7 @@ gpui = { path = "../gpui" }
 install_cli = { path = "../install_cli" }
 language = { path = "../language" }
 menu = { path = "../menu" }
+node_runtime = { path = "../node_runtime" }
 project = { path = "../project" }
 settings = { path = "../settings" }
 terminal = { path = "../terminal" }

crates/workspace/src/workspace.rs 🔗

@@ -42,6 +42,7 @@ use gpui::{
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
 use itertools::Itertools;
 use language::{LanguageRegistry, Rope};
+use node_runtime::NodeRuntime;
 use std::{
     any::TypeId,
     borrow::Cow,
@@ -456,6 +457,7 @@ pub struct AppState {
     pub initialize_workspace:
         fn(WeakViewHandle<Workspace>, bool, Arc<AppState>, AsyncAppContext) -> Task<Result<()>>,
     pub background_actions: BackgroundActions,
+    pub node_runtime: Arc<dyn NodeRuntime>,
 }
 
 pub struct WorkspaceStore {
@@ -474,6 +476,7 @@ struct Follower {
 impl AppState {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test(cx: &mut AppContext) -> Arc<Self> {
+        use node_runtime::FakeNodeRuntime;
         use settings::SettingsStore;
 
         if !cx.has_global::<SettingsStore>() {
@@ -498,6 +501,7 @@ impl AppState {
             user_store,
             // channel_store,
             workspace_store,
+            node_runtime: FakeNodeRuntime::new(),
             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
             build_window_options: |_, _, _| Default::default(),
             background_actions: || &[],
@@ -816,6 +820,7 @@ impl Workspace {
     )> {
         let project_handle = Project::local(
             app_state.client.clone(),
+            app_state.node_runtime.clone(),
             app_state.user_store.clone(),
             app_state.languages.clone(),
             app_state.fs.clone(),
@@ -3517,6 +3522,8 @@ impl Workspace {
 
     #[cfg(any(test, feature = "test-support"))]
     pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
+        use node_runtime::FakeNodeRuntime;
+
         let client = project.read(cx).client();
         let user_store = project.read(cx).user_store();
 
@@ -3530,6 +3537,7 @@ impl Workspace {
             build_window_options: |_, _, _| Default::default(),
             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
             background_actions: || &[],
+            node_runtime: FakeNodeRuntime::new(),
         });
         Self::new(0, project, app_state, cx)
     }

crates/zed/src/main.rs 🔗

@@ -138,7 +138,7 @@ fn main() {
 
         theme::init(Assets, cx);
         context_menu::init(cx);
-        project::Project::init(&client, Some(Arc::clone(&node_runtime)), cx);
+        project::Project::init(&client, cx);
         client::init(&client, cx);
         command_palette::init(cx);
         language::init(cx);
@@ -154,7 +154,12 @@ fn main() {
         semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
         vim::init(cx);
         terminal_view::init(cx);
-        copilot::init(copilot_language_server_id, http.clone(), node_runtime, cx);
+        copilot::init(
+            copilot_language_server_id,
+            http.clone(),
+            node_runtime.clone(),
+            cx,
+        );
         assistant::init(cx);
         component_test::init(cx);
 
@@ -181,6 +186,7 @@ fn main() {
             initialize_workspace,
             background_actions,
             workspace_store,
+            node_runtime,
         });
         cx.set_global(Arc::downgrade(&app_state));