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