Headless extensions (#14538)

Conrad Irwin created

Release Notes:

- remoting (alpha only): Fix extension installation

Change summary

Cargo.lock                      |  1 
crates/headless/Cargo.toml      |  1 
crates/headless/src/headless.rs |  8 +++++
crates/zed/src/main.rs          | 48 ++++++++++++++++------------------
4 files changed, 32 insertions(+), 26 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5069,6 +5069,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "client",
+ "extension",
  "fs",
  "futures 0.3.28",
  "gpui",

crates/headless/Cargo.toml 🔗

@@ -15,6 +15,7 @@ doctest = false
 [dependencies]
 anyhow.workspace = true
 client.workspace = true
+extension.workspace = true
 signal-hook.workspace = true
 gpui.workspace = true
 log.workspace = true

crates/headless/src/headless.rs 🔗

@@ -1,6 +1,7 @@
 use anyhow::{anyhow, Result};
 use client::DevServerProjectId;
 use client::{user::UserStore, Client, ClientSettings};
+use extension::ExtensionStore;
 use fs::Fs;
 use futures::Future;
 use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, Task, WeakModel};
@@ -9,7 +10,7 @@ use node_runtime::NodeRuntime;
 use postage::stream::Stream;
 use project::Project;
 use rpc::{proto, ErrorCode, TypedEnvelope};
-use settings::Settings;
+use settings::{Settings, SettingsStore};
 use std::{collections::HashMap, sync::Arc};
 use util::{ResultExt, TryFutureExt};
 
@@ -83,6 +84,11 @@ impl DevServer {
             move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err()
         });
 
+        cx.observe_global::<SettingsStore>(|_, cx| {
+            ExtensionStore::global(cx).update(cx, |store, cx| store.auto_install_extensions(cx))
+        })
+        .detach();
+
         DevServer {
             _subscriptions: vec![
                 client.add_message_handler(cx.weak_model(), Self::handle_dev_server_instructions),

crates/zed/src/main.rs 🔗

@@ -158,6 +158,26 @@ fn init_headless(
     )
 }
 
+// init_common is called for both headless and normal mode.
+fn init_common(app_state: Arc<AppState>, cx: &mut AppContext) {
+    SystemAppearance::init(cx);
+    theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
+    command_palette::init(cx);
+    snippet_provider::init(cx);
+    supermaven::init(app_state.client.clone(), cx);
+    inline_completion_registry::init(app_state.client.telemetry().clone(), cx);
+    assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
+    repl::init(cx);
+    extension::init(
+        app_state.fs.clone(),
+        app_state.client.clone(),
+        app_state.node_runtime.clone(),
+        app_state.languages.clone(),
+        ThemeRegistry::global(cx),
+        cx,
+    );
+}
+
 fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
     match cx.try_global::<AppMode>() {
         Some(AppMode::Headless(_)) => {
@@ -172,26 +192,24 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
         }
     };
 
-    SystemAppearance::init(cx);
     load_embedded_fonts(cx);
 
     #[cfg(target_os = "linux")]
     crate::zed::linux_prompts::init(cx);
 
-    theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
     app_state.languages.set_theme(cx.theme().clone());
-    command_palette::init(cx);
     editor::init(cx);
     image_viewer::init(cx);
     diagnostics::init(cx);
 
     audio::init(Assets, cx);
     workspace::init(app_state.clone(), cx);
-    recent_projects::init(cx);
 
+    recent_projects::init(cx);
     go_to_line::init(cx);
     file_finder::init(cx);
     tab_switcher::init(cx);
+    dev_server_projects::init(app_state.client.clone(), cx);
     outline::init(cx);
     project_symbols::init(cx);
     project_panel::init(Assets, cx);
@@ -201,7 +219,6 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
     search::init(cx);
     vim::init(cx);
     terminal_view::init(cx);
-
     journal::init(app_state.clone(), cx);
     language_selector::init(cx);
     theme_selector::init(cx);
@@ -213,7 +230,6 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
     markdown_preview::init(cx);
     welcome::init(cx);
     extensions_ui::init(cx);
-    snippet_provider::init(cx);
 
     // Initialize each completion provider. Settings are used for toggling between them.
     let copilot_language_server_id = app_state.languages.next_language_server_id();
@@ -223,13 +239,6 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
         app_state.node_runtime.clone(),
         cx,
     );
-    supermaven::init(app_state.client.clone(), cx);
-
-    inline_completion_registry::init(app_state.client.telemetry().clone(), cx);
-
-    assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
-
-    repl::init(cx);
 
     cx.observe_global::<SettingsStore>({
         let languages = app_state.languages.clone();
@@ -261,17 +270,6 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
     telemetry.report_setting_event("keymap", BaseKeymap::get_global(cx).to_string());
     telemetry.flush_events();
 
-    extension::init(
-        app_state.fs.clone(),
-        app_state.client.clone(),
-        app_state.node_runtime.clone(),
-        app_state.languages.clone(),
-        ThemeRegistry::global(cx),
-        cx,
-    );
-
-    dev_server_projects::init(app_state.client.clone(), cx);
-
     let fs = app_state.fs.clone();
     load_user_themes_in_background(fs.clone(), cx);
     watch_themes(fs.clone(), cx);
@@ -443,8 +441,8 @@ fn main() {
         AppState::set_global(Arc::downgrade(&app_state), cx);
 
         auto_update::init(client.http_client(), cx);
-
         reliability::init(client.http_client(), installation_id, cx);
+        init_common(app_state.clone(), cx);
 
         let args = Args::parse();
         let urls: Vec<_> = args