Lazily initialize kernelspecs (#51026)

John Tur created

On Windows, the WSL VM always boots up when Zed is opened, because we
eagerly discover which jupyter kernels are installed inside WSL on
startup. This is not desirable if the REPL feature is not being used.
Defer this work to the point where we actually need to know what kernels
are installed.

Release Notes:

- N/A

Change summary

crates/repl/src/components/kernel_options.rs |  4 +++-
crates/repl/src/repl_editor.rs               |  1 +
crates/repl/src/repl_sessions_ui.rs          |  3 ++-
crates/repl/src/repl_store.rs                | 17 ++++++++++-------
4 files changed, 16 insertions(+), 9 deletions(-)

Detailed changes

crates/repl/src/components/kernel_options.rs 🔗

@@ -448,7 +448,9 @@ where
     TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
 {
     fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
-        let store = ReplStore::global(cx).read(cx);
+        let store = ReplStore::global(cx);
+        store.update(cx, |store, cx| store.ensure_kernelspecs(cx));
+        let store = store.read(cx);
 
         let all_entries = build_grouped_entries(store, self.worktree_id);
         let selected_kernelspec = store.active_kernelspec(self.worktree_id, None, cx);

crates/repl/src/repl_editor.rs 🔗

@@ -191,6 +191,7 @@ pub fn run(
     if !store.read(cx).is_enabled() {
         return Ok(());
     }
+    store.update(cx, |store, cx| store.ensure_kernelspecs(cx));
 
     let editor = editor.upgrade().context("editor was dropped")?;
     let selected_range = editor

crates/repl/src/repl_sessions_ui.rs 🔗

@@ -204,7 +204,8 @@ impl Render for ReplSessionsPage {
     fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let store = ReplStore::global(cx);
 
-        let (kernel_specifications, sessions) = store.update(cx, |store, _cx| {
+        let (kernel_specifications, sessions) = store.update(cx, |store, cx| {
+            store.ensure_kernelspecs(cx);
             (
                 store
                     .pure_jupyter_kernel_specifications()

crates/repl/src/repl_store.rs 🔗

@@ -27,6 +27,7 @@ pub struct ReplStore {
     enabled: bool,
     sessions: HashMap<EntityId, Entity<Session>>,
     kernel_specifications: Vec<KernelSpecification>,
+    kernelspecs_initialized: bool,
     selected_kernel_for_worktree: HashMap<WorktreeId, KernelSpecification>,
     kernel_specifications_for_worktree: HashMap<WorktreeId, Vec<KernelSpecification>>,
     active_python_toolchain_for_worktree: HashMap<WorktreeId, SharedString>,
@@ -39,12 +40,6 @@ impl ReplStore {
 
     pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut App) {
         let store = cx.new(move |cx| Self::new(fs, cx));
-
-        #[cfg(not(feature = "test-support"))]
-        store
-            .update(cx, |store, cx| store.refresh_kernelspecs(cx))
-            .detach_and_log_err(cx);
-
         cx.set_global(GlobalReplStore(store))
     }
 
@@ -65,6 +60,7 @@ impl ReplStore {
             enabled: JupyterSettings::enabled(cx),
             sessions: HashMap::default(),
             kernel_specifications: Vec::new(),
+            kernelspecs_initialized: false,
             _subscriptions: subscriptions,
             kernel_specifications_for_worktree: HashMap::default(),
             selected_kernel_for_worktree: HashMap::default(),
@@ -216,10 +212,17 @@ impl ReplStore {
         }
     }
 
+    pub fn ensure_kernelspecs(&mut self, cx: &mut Context<Self>) {
+        if self.kernelspecs_initialized {
+            return;
+        }
+        self.kernelspecs_initialized = true;
+        self.refresh_kernelspecs(cx).detach_and_log_err(cx);
+    }
+
     pub fn refresh_kernelspecs(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
         let local_kernel_specifications = local_kernel_specifications(self.fs.clone());
         let wsl_kernel_specifications = wsl_kernel_specifications(cx.background_executor().clone());
-
         let remote_kernel_specifications = self.get_remote_kernel_specifications(cx);
 
         let all_specs = cx.background_spawn(async move {