extension_registration_hooks.rs

  1use std::{path::PathBuf, sync::Arc};
  2
  3use anyhow::{anyhow, Result};
  4use assistant_slash_command::{ExtensionSlashCommand, SlashCommandRegistry};
  5use context_servers::manager::ServerCommand;
  6use context_servers::ContextServerFactoryRegistry;
  7use db::smol::future::FutureExt as _;
  8use extension::Extension;
  9use extension_host::wasm_host::ExtensionProject;
 10use extension_host::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host};
 11use fs::Fs;
 12use gpui::{AppContext, BackgroundExecutor, Model, Task};
 13use indexed_docs::{ExtensionIndexedDocsProvider, IndexedDocsRegistry, ProviderId};
 14use language::{LanguageRegistry, LanguageServerBinaryStatus, LoadedLanguage};
 15use snippet_provider::SnippetRegistry;
 16use theme::{ThemeRegistry, ThemeSettings};
 17use ui::SharedString;
 18use wasmtime_wasi::WasiView as _;
 19
 20pub struct ConcreteExtensionRegistrationHooks {
 21    slash_command_registry: Arc<SlashCommandRegistry>,
 22    theme_registry: Arc<ThemeRegistry>,
 23    indexed_docs_registry: Arc<IndexedDocsRegistry>,
 24    snippet_registry: Arc<SnippetRegistry>,
 25    language_registry: Arc<LanguageRegistry>,
 26    context_server_factory_registry: Model<ContextServerFactoryRegistry>,
 27    executor: BackgroundExecutor,
 28}
 29
 30impl ConcreteExtensionRegistrationHooks {
 31    pub fn new(
 32        theme_registry: Arc<ThemeRegistry>,
 33        slash_command_registry: Arc<SlashCommandRegistry>,
 34        indexed_docs_registry: Arc<IndexedDocsRegistry>,
 35        snippet_registry: Arc<SnippetRegistry>,
 36        language_registry: Arc<LanguageRegistry>,
 37        context_server_factory_registry: Model<ContextServerFactoryRegistry>,
 38        cx: &AppContext,
 39    ) -> Arc<dyn extension_host::ExtensionRegistrationHooks> {
 40        Arc::new(Self {
 41            theme_registry,
 42            slash_command_registry,
 43            indexed_docs_registry,
 44            snippet_registry,
 45            language_registry,
 46            context_server_factory_registry,
 47            executor: cx.background_executor().clone(),
 48        })
 49    }
 50}
 51
 52impl extension_host::ExtensionRegistrationHooks for ConcreteExtensionRegistrationHooks {
 53    fn remove_user_themes(&self, themes: Vec<SharedString>) {
 54        self.theme_registry.remove_user_themes(&themes);
 55    }
 56
 57    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn fs::Fs>) -> Task<Result<()>> {
 58        let theme_registry = self.theme_registry.clone();
 59        self.executor
 60            .spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
 61    }
 62
 63    fn register_slash_command(
 64        &self,
 65        extension: Arc<dyn Extension>,
 66        command: extension::SlashCommand,
 67    ) {
 68        self.slash_command_registry
 69            .register_command(ExtensionSlashCommand::new(extension, command), false)
 70    }
 71
 72    fn register_context_server(
 73        &self,
 74        id: Arc<str>,
 75        extension: wasm_host::WasmExtension,
 76        cx: &mut AppContext,
 77    ) {
 78        self.context_server_factory_registry
 79            .update(cx, |registry, _| {
 80                registry.register_server_factory(
 81                    id.clone(),
 82                    Arc::new({
 83                        move |project, cx| {
 84                            log::info!(
 85                                "loading command for context server {id} from extension {}",
 86                                extension.manifest.id
 87                            );
 88
 89                            let id = id.clone();
 90                            let extension = extension.clone();
 91                            cx.spawn(|mut cx| async move {
 92                                let extension_project =
 93                                    project.update(&mut cx, |project, cx| ExtensionProject {
 94                                        worktree_ids: project
 95                                            .visible_worktrees(cx)
 96                                            .map(|worktree| worktree.read(cx).id().to_proto())
 97                                            .collect(),
 98                                    })?;
 99
100                                let command = extension
101                                    .call({
102                                        let id = id.clone();
103                                        |extension, store| {
104                                            async move {
105                                                let project = store
106                                                    .data_mut()
107                                                    .table()
108                                                    .push(extension_project)?;
109                                                let command = extension
110                                                    .call_context_server_command(
111                                                        store,
112                                                        id.clone(),
113                                                        project,
114                                                    )
115                                                    .await?
116                                                    .map_err(|e| anyhow!("{}", e))?;
117                                                anyhow::Ok(command)
118                                            }
119                                            .boxed()
120                                        }
121                                    })
122                                    .await?;
123
124                                log::info!("loaded command for context server {id}: {command:?}");
125
126                                Ok(ServerCommand {
127                                    path: command.command,
128                                    args: command.args,
129                                    env: Some(command.env.into_iter().collect()),
130                                })
131                            })
132                        }
133                    }),
134                )
135            });
136    }
137
138    fn register_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
139        self.indexed_docs_registry
140            .register_provider(Box::new(ExtensionIndexedDocsProvider::new(
141                extension,
142                ProviderId(provider_id),
143            )));
144    }
145
146    fn register_snippets(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
147        self.snippet_registry
148            .register_snippets(path, snippet_contents)
149    }
150
151    fn update_lsp_status(
152        &self,
153        server_name: lsp::LanguageServerName,
154        status: LanguageServerBinaryStatus,
155    ) {
156        self.language_registry
157            .update_lsp_status(server_name, status);
158    }
159
160    fn register_lsp_adapter(
161        &self,
162        language_name: language::LanguageName,
163        adapter: ExtensionLspAdapter,
164    ) {
165        self.language_registry
166            .register_lsp_adapter(language_name, Arc::new(adapter));
167    }
168
169    fn remove_lsp_adapter(
170        &self,
171        language_name: &language::LanguageName,
172        server_name: &lsp::LanguageServerName,
173    ) {
174        self.language_registry
175            .remove_lsp_adapter(language_name, server_name);
176    }
177
178    fn remove_languages(
179        &self,
180        languages_to_remove: &[language::LanguageName],
181        grammars_to_remove: &[Arc<str>],
182    ) {
183        self.language_registry
184            .remove_languages(&languages_to_remove, &grammars_to_remove);
185    }
186
187    fn register_wasm_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
188        self.language_registry.register_wasm_grammars(grammars)
189    }
190
191    fn register_language(
192        &self,
193        language: language::LanguageName,
194        grammar: Option<Arc<str>>,
195        matcher: language::LanguageMatcher,
196        load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
197    ) {
198        self.language_registry
199            .register_language(language, grammar, matcher, load)
200    }
201
202    fn reload_current_theme(&self, cx: &mut AppContext) {
203        ThemeSettings::reload_current_theme(cx)
204    }
205
206    fn list_theme_names(&self, path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
207        self.executor.spawn(async move {
208            let themes = theme::read_user_theme(&path, fs).await?;
209            Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
210        })
211    }
212}