extension_host_proxy.rs

  1use std::path::{Path, PathBuf};
  2use std::sync::Arc;
  3
  4use anyhow::Result;
  5use fs::Fs;
  6use gpui::{App, Global, ReadGlobal, SharedString, Task};
  7use language::{BinaryStatus, LanguageMatcher, LanguageName, LoadedLanguage};
  8use lsp::LanguageServerName;
  9use parking_lot::RwLock;
 10
 11use crate::{Extension, SlashCommand};
 12
 13#[derive(Default)]
 14struct GlobalExtensionHostProxy(Arc<ExtensionHostProxy>);
 15
 16impl Global for GlobalExtensionHostProxy {}
 17
 18/// A proxy for interacting with the extension host.
 19///
 20/// This object implements each of the individual proxy types so that their
 21/// methods can be called directly on it.
 22#[derive(Default)]
 23pub struct ExtensionHostProxy {
 24    theme_proxy: RwLock<Option<Arc<dyn ExtensionThemeProxy>>>,
 25    grammar_proxy: RwLock<Option<Arc<dyn ExtensionGrammarProxy>>>,
 26    language_proxy: RwLock<Option<Arc<dyn ExtensionLanguageProxy>>>,
 27    language_server_proxy: RwLock<Option<Arc<dyn ExtensionLanguageServerProxy>>>,
 28    snippet_proxy: RwLock<Option<Arc<dyn ExtensionSnippetProxy>>>,
 29    slash_command_proxy: RwLock<Option<Arc<dyn ExtensionSlashCommandProxy>>>,
 30    context_server_proxy: RwLock<Option<Arc<dyn ExtensionContextServerProxy>>>,
 31    debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
 32    language_model_provider_proxy: RwLock<Option<Arc<dyn ExtensionLanguageModelProviderProxy>>>,
 33}
 34
 35impl ExtensionHostProxy {
 36    /// Returns the global [`ExtensionHostProxy`].
 37    pub fn global(cx: &App) -> Arc<Self> {
 38        GlobalExtensionHostProxy::global(cx).0.clone()
 39    }
 40
 41    /// Returns the global [`ExtensionHostProxy`].
 42    ///
 43    /// Inserts a default [`ExtensionHostProxy`] if one does not yet exist.
 44    pub fn default_global(cx: &mut App) -> Arc<Self> {
 45        cx.default_global::<GlobalExtensionHostProxy>().0.clone()
 46    }
 47
 48    pub fn new() -> Self {
 49        Self {
 50            theme_proxy: RwLock::default(),
 51            grammar_proxy: RwLock::default(),
 52            language_proxy: RwLock::default(),
 53            language_server_proxy: RwLock::default(),
 54            snippet_proxy: RwLock::default(),
 55            slash_command_proxy: RwLock::default(),
 56            context_server_proxy: RwLock::default(),
 57            debug_adapter_provider_proxy: RwLock::default(),
 58            language_model_provider_proxy: RwLock::default(),
 59        }
 60    }
 61
 62    pub fn register_theme_proxy(&self, proxy: impl ExtensionThemeProxy) {
 63        self.theme_proxy.write().replace(Arc::new(proxy));
 64    }
 65
 66    pub fn register_grammar_proxy(&self, proxy: impl ExtensionGrammarProxy) {
 67        self.grammar_proxy.write().replace(Arc::new(proxy));
 68    }
 69
 70    pub fn register_language_proxy(&self, proxy: impl ExtensionLanguageProxy) {
 71        self.language_proxy.write().replace(Arc::new(proxy));
 72    }
 73
 74    pub fn register_language_server_proxy(&self, proxy: impl ExtensionLanguageServerProxy) {
 75        self.language_server_proxy.write().replace(Arc::new(proxy));
 76    }
 77
 78    pub fn register_snippet_proxy(&self, proxy: impl ExtensionSnippetProxy) {
 79        self.snippet_proxy.write().replace(Arc::new(proxy));
 80    }
 81
 82    pub fn register_slash_command_proxy(&self, proxy: impl ExtensionSlashCommandProxy) {
 83        self.slash_command_proxy.write().replace(Arc::new(proxy));
 84    }
 85
 86    pub fn register_context_server_proxy(&self, proxy: impl ExtensionContextServerProxy) {
 87        self.context_server_proxy.write().replace(Arc::new(proxy));
 88    }
 89
 90    pub fn register_debug_adapter_proxy(&self, proxy: impl ExtensionDebugAdapterProviderProxy) {
 91        self.debug_adapter_provider_proxy
 92            .write()
 93            .replace(Arc::new(proxy));
 94    }
 95
 96    pub fn register_language_model_provider_proxy(
 97        &self,
 98        proxy: impl ExtensionLanguageModelProviderProxy,
 99    ) {
100        self.language_model_provider_proxy
101            .write()
102            .replace(Arc::new(proxy));
103    }
104}
105
106pub trait ExtensionThemeProxy: Send + Sync + 'static {
107    fn set_extensions_loaded(&self);
108
109    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
110
111    fn remove_user_themes(&self, themes: Vec<SharedString>);
112
113    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
114
115    fn reload_current_theme(&self, cx: &mut App);
116
117    fn list_icon_theme_names(
118        &self,
119        icon_theme_path: PathBuf,
120        fs: Arc<dyn Fs>,
121    ) -> Task<Result<Vec<String>>>;
122
123    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
124
125    fn load_icon_theme(
126        &self,
127        icon_theme_path: PathBuf,
128        icons_root_dir: PathBuf,
129        fs: Arc<dyn Fs>,
130    ) -> Task<Result<()>>;
131
132    fn reload_current_icon_theme(&self, cx: &mut App);
133}
134
135impl ExtensionThemeProxy for ExtensionHostProxy {
136    fn set_extensions_loaded(&self) {
137        let Some(proxy) = self.theme_proxy.read().clone() else {
138            return;
139        };
140
141        proxy.set_extensions_loaded()
142    }
143
144    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
145        let Some(proxy) = self.theme_proxy.read().clone() else {
146            return Task::ready(Ok(Vec::new()));
147        };
148
149        proxy.list_theme_names(theme_path, fs)
150    }
151
152    fn remove_user_themes(&self, themes: Vec<SharedString>) {
153        let Some(proxy) = self.theme_proxy.read().clone() else {
154            return;
155        };
156
157        proxy.remove_user_themes(themes)
158    }
159
160    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
161        let Some(proxy) = self.theme_proxy.read().clone() else {
162            return Task::ready(Ok(()));
163        };
164
165        proxy.load_user_theme(theme_path, fs)
166    }
167
168    fn reload_current_theme(&self, cx: &mut App) {
169        let Some(proxy) = self.theme_proxy.read().clone() else {
170            return;
171        };
172
173        proxy.reload_current_theme(cx)
174    }
175
176    fn list_icon_theme_names(
177        &self,
178        icon_theme_path: PathBuf,
179        fs: Arc<dyn Fs>,
180    ) -> Task<Result<Vec<String>>> {
181        let Some(proxy) = self.theme_proxy.read().clone() else {
182            return Task::ready(Ok(Vec::new()));
183        };
184
185        proxy.list_icon_theme_names(icon_theme_path, fs)
186    }
187
188    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
189        let Some(proxy) = self.theme_proxy.read().clone() else {
190            return;
191        };
192
193        proxy.remove_icon_themes(icon_themes)
194    }
195
196    fn load_icon_theme(
197        &self,
198        icon_theme_path: PathBuf,
199        icons_root_dir: PathBuf,
200        fs: Arc<dyn Fs>,
201    ) -> Task<Result<()>> {
202        let Some(proxy) = self.theme_proxy.read().clone() else {
203            return Task::ready(Ok(()));
204        };
205
206        proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
207    }
208
209    fn reload_current_icon_theme(&self, cx: &mut App) {
210        let Some(proxy) = self.theme_proxy.read().clone() else {
211            return;
212        };
213
214        proxy.reload_current_icon_theme(cx)
215    }
216}
217
218pub trait ExtensionGrammarProxy: Send + Sync + 'static {
219    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
220}
221
222impl ExtensionGrammarProxy for ExtensionHostProxy {
223    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
224        let Some(proxy) = self.grammar_proxy.read().clone() else {
225            return;
226        };
227
228        proxy.register_grammars(grammars)
229    }
230}
231
232pub trait ExtensionLanguageProxy: Send + Sync + 'static {
233    fn register_language(
234        &self,
235        language: LanguageName,
236        grammar: Option<Arc<str>>,
237        matcher: LanguageMatcher,
238        hidden: bool,
239        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
240    );
241
242    fn remove_languages(
243        &self,
244        languages_to_remove: &[LanguageName],
245        grammars_to_remove: &[Arc<str>],
246    );
247}
248
249impl ExtensionLanguageProxy for ExtensionHostProxy {
250    fn register_language(
251        &self,
252        language: LanguageName,
253        grammar: Option<Arc<str>>,
254        matcher: LanguageMatcher,
255        hidden: bool,
256        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
257    ) {
258        let Some(proxy) = self.language_proxy.read().clone() else {
259            return;
260        };
261
262        proxy.register_language(language, grammar, matcher, hidden, load)
263    }
264
265    fn remove_languages(
266        &self,
267        languages_to_remove: &[LanguageName],
268        grammars_to_remove: &[Arc<str>],
269    ) {
270        let Some(proxy) = self.language_proxy.read().clone() else {
271            return;
272        };
273
274        proxy.remove_languages(languages_to_remove, grammars_to_remove)
275    }
276}
277
278pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
279    fn register_language_server(
280        &self,
281        extension: Arc<dyn Extension>,
282        language_server_id: LanguageServerName,
283        language: LanguageName,
284    );
285
286    fn remove_language_server(
287        &self,
288        language: &LanguageName,
289        language_server_id: &LanguageServerName,
290        cx: &mut App,
291    ) -> Task<Result<()>>;
292
293    fn update_language_server_status(
294        &self,
295        language_server_id: LanguageServerName,
296        status: BinaryStatus,
297    );
298}
299
300impl ExtensionLanguageServerProxy for ExtensionHostProxy {
301    fn register_language_server(
302        &self,
303        extension: Arc<dyn Extension>,
304        language_server_id: LanguageServerName,
305        language: LanguageName,
306    ) {
307        let Some(proxy) = self.language_server_proxy.read().clone() else {
308            return;
309        };
310
311        proxy.register_language_server(extension, language_server_id, language)
312    }
313
314    fn remove_language_server(
315        &self,
316        language: &LanguageName,
317        language_server_id: &LanguageServerName,
318        cx: &mut App,
319    ) -> Task<Result<()>> {
320        let Some(proxy) = self.language_server_proxy.read().clone() else {
321            return Task::ready(Ok(()));
322        };
323
324        proxy.remove_language_server(language, language_server_id, cx)
325    }
326
327    fn update_language_server_status(
328        &self,
329        language_server_id: LanguageServerName,
330        status: BinaryStatus,
331    ) {
332        let Some(proxy) = self.language_server_proxy.read().clone() else {
333            return;
334        };
335
336        proxy.update_language_server_status(language_server_id, status)
337    }
338}
339
340pub trait ExtensionSnippetProxy: Send + Sync + 'static {
341    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
342}
343
344impl ExtensionSnippetProxy for ExtensionHostProxy {
345    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
346        let Some(proxy) = self.snippet_proxy.read().clone() else {
347            return Ok(());
348        };
349
350        proxy.register_snippet(path, snippet_contents)
351    }
352}
353
354pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
355    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
356
357    fn unregister_slash_command(&self, command_name: Arc<str>);
358}
359
360impl ExtensionSlashCommandProxy for ExtensionHostProxy {
361    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
362        let Some(proxy) = self.slash_command_proxy.read().clone() else {
363            return;
364        };
365
366        proxy.register_slash_command(extension, command)
367    }
368
369    fn unregister_slash_command(&self, command_name: Arc<str>) {
370        let Some(proxy) = self.slash_command_proxy.read().clone() else {
371            return;
372        };
373
374        proxy.unregister_slash_command(command_name)
375    }
376}
377
378pub trait ExtensionContextServerProxy: Send + Sync + 'static {
379    fn register_context_server(
380        &self,
381        extension: Arc<dyn Extension>,
382        server_id: Arc<str>,
383        cx: &mut App,
384    );
385
386    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
387}
388
389/// A function that registers a language model provider with the registry.
390/// This allows extension_host to create the provider (which requires WasmExtension)
391/// and pass a registration closure to the language_models crate.
392pub type LanguageModelProviderRegistration = Box<dyn FnOnce(&mut App) + Send + Sync + 'static>;
393
394pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
395    /// Register an LLM provider from an extension.
396    /// The `register_fn` closure will be called with the App context and should
397    /// register the provider with the LanguageModelRegistry.
398    fn register_language_model_provider(
399        &self,
400        provider_id: Arc<str>,
401        register_fn: LanguageModelProviderRegistration,
402        cx: &mut App,
403    );
404
405    /// Unregister an LLM provider when an extension is unloaded.
406    fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
407}
408
409impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
410    fn register_language_model_provider(
411        &self,
412        provider_id: Arc<str>,
413        register_fn: LanguageModelProviderRegistration,
414        cx: &mut App,
415    ) {
416        let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
417            return;
418        };
419
420        proxy.register_language_model_provider(provider_id, register_fn, cx)
421    }
422
423    fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
424        let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
425            return;
426        };
427
428        proxy.unregister_language_model_provider(provider_id, cx)
429    }
430}
431
432impl ExtensionContextServerProxy for ExtensionHostProxy {
433    fn register_context_server(
434        &self,
435        extension: Arc<dyn Extension>,
436        server_id: Arc<str>,
437        cx: &mut App,
438    ) {
439        let Some(proxy) = self.context_server_proxy.read().clone() else {
440            return;
441        };
442
443        proxy.register_context_server(extension, server_id, cx)
444    }
445
446    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
447        let Some(proxy) = self.context_server_proxy.read().clone() else {
448            return;
449        };
450
451        proxy.unregister_context_server(server_id, cx)
452    }
453}
454
455pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
456    fn register_debug_adapter(
457        &self,
458        extension: Arc<dyn Extension>,
459        debug_adapter_name: Arc<str>,
460        schema_path: &Path,
461    );
462    fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>);
463    fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>);
464    fn unregister_debug_locator(&self, locator_name: Arc<str>);
465}
466
467impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
468    fn register_debug_adapter(
469        &self,
470        extension: Arc<dyn Extension>,
471        debug_adapter_name: Arc<str>,
472        schema_path: &Path,
473    ) {
474        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
475            return;
476        };
477
478        proxy.register_debug_adapter(extension, debug_adapter_name, schema_path)
479    }
480
481    fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>) {
482        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
483            return;
484        };
485
486        proxy.register_debug_locator(extension, locator_name)
487    }
488    fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>) {
489        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
490            return;
491        };
492
493        proxy.unregister_debug_adapter(debug_adapter_name)
494    }
495    fn unregister_debug_locator(&self, locator_name: Arc<str>) {
496        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
497            return;
498        };
499
500        proxy.unregister_debug_locator(locator_name)
501    }
502}