extension_host_proxy.rs

  1use std::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    indexed_docs_provider_proxy: RwLock<Option<Arc<dyn ExtensionIndexedDocsProviderProxy>>>,
 32    debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
 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            indexed_docs_provider_proxy: RwLock::default(),
 58            debug_adapter_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_indexed_docs_provider_proxy(
 91        &self,
 92        proxy: impl ExtensionIndexedDocsProviderProxy,
 93    ) {
 94        self.indexed_docs_provider_proxy
 95            .write()
 96            .replace(Arc::new(proxy));
 97    }
 98    pub fn register_debug_adapter_proxy(&self, proxy: impl ExtensionDebugAdapterProviderProxy) {
 99        self.debug_adapter_provider_proxy
100            .write()
101            .replace(Arc::new(proxy));
102    }
103}
104
105pub trait ExtensionThemeProxy: Send + Sync + 'static {
106    fn set_extensions_loaded(&self);
107
108    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
109
110    fn remove_user_themes(&self, themes: Vec<SharedString>);
111
112    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
113
114    fn reload_current_theme(&self, cx: &mut App);
115
116    fn list_icon_theme_names(
117        &self,
118        icon_theme_path: PathBuf,
119        fs: Arc<dyn Fs>,
120    ) -> Task<Result<Vec<String>>>;
121
122    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
123
124    fn load_icon_theme(
125        &self,
126        icon_theme_path: PathBuf,
127        icons_root_dir: PathBuf,
128        fs: Arc<dyn Fs>,
129    ) -> Task<Result<()>>;
130
131    fn reload_current_icon_theme(&self, cx: &mut App);
132}
133
134impl ExtensionThemeProxy for ExtensionHostProxy {
135    fn set_extensions_loaded(&self) {
136        let Some(proxy) = self.theme_proxy.read().clone() else {
137            return;
138        };
139
140        proxy.set_extensions_loaded()
141    }
142
143    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
144        let Some(proxy) = self.theme_proxy.read().clone() else {
145            return Task::ready(Ok(Vec::new()));
146        };
147
148        proxy.list_theme_names(theme_path, fs)
149    }
150
151    fn remove_user_themes(&self, themes: Vec<SharedString>) {
152        let Some(proxy) = self.theme_proxy.read().clone() else {
153            return;
154        };
155
156        proxy.remove_user_themes(themes)
157    }
158
159    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
160        let Some(proxy) = self.theme_proxy.read().clone() else {
161            return Task::ready(Ok(()));
162        };
163
164        proxy.load_user_theme(theme_path, fs)
165    }
166
167    fn reload_current_theme(&self, cx: &mut App) {
168        let Some(proxy) = self.theme_proxy.read().clone() else {
169            return;
170        };
171
172        proxy.reload_current_theme(cx)
173    }
174
175    fn list_icon_theme_names(
176        &self,
177        icon_theme_path: PathBuf,
178        fs: Arc<dyn Fs>,
179    ) -> Task<Result<Vec<String>>> {
180        let Some(proxy) = self.theme_proxy.read().clone() else {
181            return Task::ready(Ok(Vec::new()));
182        };
183
184        proxy.list_icon_theme_names(icon_theme_path, fs)
185    }
186
187    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
188        let Some(proxy) = self.theme_proxy.read().clone() else {
189            return;
190        };
191
192        proxy.remove_icon_themes(icon_themes)
193    }
194
195    fn load_icon_theme(
196        &self,
197        icon_theme_path: PathBuf,
198        icons_root_dir: PathBuf,
199        fs: Arc<dyn Fs>,
200    ) -> Task<Result<()>> {
201        let Some(proxy) = self.theme_proxy.read().clone() else {
202            return Task::ready(Ok(()));
203        };
204
205        proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
206    }
207
208    fn reload_current_icon_theme(&self, cx: &mut App) {
209        let Some(proxy) = self.theme_proxy.read().clone() else {
210            return;
211        };
212
213        proxy.reload_current_icon_theme(cx)
214    }
215}
216
217pub trait ExtensionGrammarProxy: Send + Sync + 'static {
218    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
219}
220
221impl ExtensionGrammarProxy for ExtensionHostProxy {
222    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
223        let Some(proxy) = self.grammar_proxy.read().clone() else {
224            return;
225        };
226
227        proxy.register_grammars(grammars)
228    }
229}
230
231pub trait ExtensionLanguageProxy: Send + Sync + 'static {
232    fn register_language(
233        &self,
234        language: LanguageName,
235        grammar: Option<Arc<str>>,
236        matcher: LanguageMatcher,
237        hidden: bool,
238        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
239    );
240
241    fn remove_languages(
242        &self,
243        languages_to_remove: &[LanguageName],
244        grammars_to_remove: &[Arc<str>],
245    );
246}
247
248impl ExtensionLanguageProxy for ExtensionHostProxy {
249    fn register_language(
250        &self,
251        language: LanguageName,
252        grammar: Option<Arc<str>>,
253        matcher: LanguageMatcher,
254        hidden: bool,
255        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
256    ) {
257        let Some(proxy) = self.language_proxy.read().clone() else {
258            return;
259        };
260
261        proxy.register_language(language, grammar, matcher, hidden, load)
262    }
263
264    fn remove_languages(
265        &self,
266        languages_to_remove: &[LanguageName],
267        grammars_to_remove: &[Arc<str>],
268    ) {
269        let Some(proxy) = self.language_proxy.read().clone() else {
270            return;
271        };
272
273        proxy.remove_languages(languages_to_remove, grammars_to_remove)
274    }
275}
276
277pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
278    fn register_language_server(
279        &self,
280        extension: Arc<dyn Extension>,
281        language_server_id: LanguageServerName,
282        language: LanguageName,
283    );
284
285    fn remove_language_server(
286        &self,
287        language: &LanguageName,
288        language_server_id: &LanguageServerName,
289    );
290
291    fn update_language_server_status(
292        &self,
293        language_server_id: LanguageServerName,
294        status: BinaryStatus,
295    );
296}
297
298impl ExtensionLanguageServerProxy for ExtensionHostProxy {
299    fn register_language_server(
300        &self,
301        extension: Arc<dyn Extension>,
302        language_server_id: LanguageServerName,
303        language: LanguageName,
304    ) {
305        let Some(proxy) = self.language_server_proxy.read().clone() else {
306            return;
307        };
308
309        proxy.register_language_server(extension, language_server_id, language)
310    }
311
312    fn remove_language_server(
313        &self,
314        language: &LanguageName,
315        language_server_id: &LanguageServerName,
316    ) {
317        let Some(proxy) = self.language_server_proxy.read().clone() else {
318            return;
319        };
320
321        proxy.remove_language_server(language, language_server_id)
322    }
323
324    fn update_language_server_status(
325        &self,
326        language_server_id: LanguageServerName,
327        status: BinaryStatus,
328    ) {
329        let Some(proxy) = self.language_server_proxy.read().clone() else {
330            return;
331        };
332
333        proxy.update_language_server_status(language_server_id, status)
334    }
335}
336
337pub trait ExtensionSnippetProxy: Send + Sync + 'static {
338    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
339}
340
341impl ExtensionSnippetProxy for ExtensionHostProxy {
342    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
343        let Some(proxy) = self.snippet_proxy.read().clone() else {
344            return Ok(());
345        };
346
347        proxy.register_snippet(path, snippet_contents)
348    }
349}
350
351pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
352    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
353}
354
355impl ExtensionSlashCommandProxy for ExtensionHostProxy {
356    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
357        let Some(proxy) = self.slash_command_proxy.read().clone() else {
358            return;
359        };
360
361        proxy.register_slash_command(extension, command)
362    }
363}
364
365pub trait ExtensionContextServerProxy: Send + Sync + 'static {
366    fn register_context_server(
367        &self,
368        extension: Arc<dyn Extension>,
369        server_id: Arc<str>,
370        cx: &mut App,
371    );
372
373    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
374}
375
376impl ExtensionContextServerProxy for ExtensionHostProxy {
377    fn register_context_server(
378        &self,
379        extension: Arc<dyn Extension>,
380        server_id: Arc<str>,
381        cx: &mut App,
382    ) {
383        let Some(proxy) = self.context_server_proxy.read().clone() else {
384            return;
385        };
386
387        proxy.register_context_server(extension, server_id, cx)
388    }
389
390    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
391        let Some(proxy) = self.context_server_proxy.read().clone() else {
392            return;
393        };
394
395        proxy.unregister_context_server(server_id, cx)
396    }
397}
398
399pub trait ExtensionIndexedDocsProviderProxy: Send + Sync + 'static {
400    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>);
401}
402
403impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
404    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
405        let Some(proxy) = self.indexed_docs_provider_proxy.read().clone() else {
406            return;
407        };
408
409        proxy.register_indexed_docs_provider(extension, provider_id)
410    }
411}
412
413pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
414    fn register_debug_adapter(&self, extension: Arc<dyn Extension>, debug_adapter_name: Arc<str>);
415}
416
417impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
418    fn register_debug_adapter(&self, extension: Arc<dyn Extension>, debug_adapter_name: Arc<str>) {
419        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
420            return;
421        };
422
423        proxy.register_debug_adapter(extension, debug_adapter_name)
424    }
425}