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, LanguageConfig, 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}
 33
 34impl ExtensionHostProxy {
 35    /// Returns the global [`ExtensionHostProxy`].
 36    pub fn global(cx: &App) -> Arc<Self> {
 37        GlobalExtensionHostProxy::global(cx).0.clone()
 38    }
 39
 40    /// Returns the global [`ExtensionHostProxy`].
 41    ///
 42    /// Inserts a default [`ExtensionHostProxy`] if one does not yet exist.
 43    pub fn default_global(cx: &mut App) -> Arc<Self> {
 44        cx.default_global::<GlobalExtensionHostProxy>().0.clone()
 45    }
 46
 47    pub fn new() -> Self {
 48        Self {
 49            theme_proxy: RwLock::default(),
 50            grammar_proxy: RwLock::default(),
 51            language_proxy: RwLock::default(),
 52            language_server_proxy: RwLock::default(),
 53            snippet_proxy: RwLock::default(),
 54            slash_command_proxy: RwLock::default(),
 55            context_server_proxy: RwLock::default(),
 56            indexed_docs_provider_proxy: RwLock::default(),
 57        }
 58    }
 59
 60    pub fn register_theme_proxy(&self, proxy: impl ExtensionThemeProxy) {
 61        self.theme_proxy.write().replace(Arc::new(proxy));
 62    }
 63
 64    pub fn register_grammar_proxy(&self, proxy: impl ExtensionGrammarProxy) {
 65        self.grammar_proxy.write().replace(Arc::new(proxy));
 66    }
 67
 68    pub fn register_language_proxy(&self, proxy: impl ExtensionLanguageProxy) {
 69        self.language_proxy.write().replace(Arc::new(proxy));
 70    }
 71
 72    pub fn register_language_server_proxy(&self, proxy: impl ExtensionLanguageServerProxy) {
 73        self.language_server_proxy.write().replace(Arc::new(proxy));
 74    }
 75
 76    pub fn register_snippet_proxy(&self, proxy: impl ExtensionSnippetProxy) {
 77        self.snippet_proxy.write().replace(Arc::new(proxy));
 78    }
 79
 80    pub fn register_slash_command_proxy(&self, proxy: impl ExtensionSlashCommandProxy) {
 81        self.slash_command_proxy.write().replace(Arc::new(proxy));
 82    }
 83
 84    pub fn register_context_server_proxy(&self, proxy: impl ExtensionContextServerProxy) {
 85        self.context_server_proxy.write().replace(Arc::new(proxy));
 86    }
 87
 88    pub fn register_indexed_docs_provider_proxy(
 89        &self,
 90        proxy: impl ExtensionIndexedDocsProviderProxy,
 91    ) {
 92        self.indexed_docs_provider_proxy
 93            .write()
 94            .replace(Arc::new(proxy));
 95    }
 96}
 97
 98pub trait ExtensionThemeProxy: Send + Sync + 'static {
 99    fn set_extensions_loaded(&self);
100
101    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
102
103    fn remove_user_themes(&self, themes: Vec<SharedString>);
104
105    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
106
107    fn reload_current_theme(&self, cx: &mut App);
108
109    fn list_icon_theme_names(
110        &self,
111        icon_theme_path: PathBuf,
112        fs: Arc<dyn Fs>,
113    ) -> Task<Result<Vec<String>>>;
114
115    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
116
117    fn load_icon_theme(
118        &self,
119        icon_theme_path: PathBuf,
120        icons_root_dir: PathBuf,
121        fs: Arc<dyn Fs>,
122    ) -> Task<Result<()>>;
123
124    fn reload_current_icon_theme(&self, cx: &mut App);
125}
126
127impl ExtensionThemeProxy for ExtensionHostProxy {
128    fn set_extensions_loaded(&self) {
129        let Some(proxy) = self.theme_proxy.read().clone() else {
130            return;
131        };
132
133        proxy.set_extensions_loaded()
134    }
135
136    fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
137        let Some(proxy) = self.theme_proxy.read().clone() else {
138            return Task::ready(Ok(Vec::new()));
139        };
140
141        proxy.list_theme_names(theme_path, fs)
142    }
143
144    fn remove_user_themes(&self, themes: Vec<SharedString>) {
145        let Some(proxy) = self.theme_proxy.read().clone() else {
146            return;
147        };
148
149        proxy.remove_user_themes(themes)
150    }
151
152    fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
153        let Some(proxy) = self.theme_proxy.read().clone() else {
154            return Task::ready(Ok(()));
155        };
156
157        proxy.load_user_theme(theme_path, fs)
158    }
159
160    fn reload_current_theme(&self, cx: &mut App) {
161        let Some(proxy) = self.theme_proxy.read().clone() else {
162            return;
163        };
164
165        proxy.reload_current_theme(cx)
166    }
167
168    fn list_icon_theme_names(
169        &self,
170        icon_theme_path: PathBuf,
171        fs: Arc<dyn Fs>,
172    ) -> Task<Result<Vec<String>>> {
173        let Some(proxy) = self.theme_proxy.read().clone() else {
174            return Task::ready(Ok(Vec::new()));
175        };
176
177        proxy.list_icon_theme_names(icon_theme_path, fs)
178    }
179
180    fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
181        let Some(proxy) = self.theme_proxy.read().clone() else {
182            return;
183        };
184
185        proxy.remove_icon_themes(icon_themes)
186    }
187
188    fn load_icon_theme(
189        &self,
190        icon_theme_path: PathBuf,
191        icons_root_dir: PathBuf,
192        fs: Arc<dyn Fs>,
193    ) -> Task<Result<()>> {
194        let Some(proxy) = self.theme_proxy.read().clone() else {
195            return Task::ready(Ok(()));
196        };
197
198        proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
199    }
200
201    fn reload_current_icon_theme(&self, cx: &mut App) {
202        let Some(proxy) = self.theme_proxy.read().clone() else {
203            return;
204        };
205
206        proxy.reload_current_icon_theme(cx)
207    }
208}
209
210pub trait ExtensionGrammarProxy: Send + Sync + 'static {
211    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
212}
213
214impl ExtensionGrammarProxy for ExtensionHostProxy {
215    fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
216        let Some(proxy) = self.grammar_proxy.read().clone() else {
217            return;
218        };
219
220        proxy.register_grammars(grammars)
221    }
222}
223
224pub trait ExtensionLanguageProxy: Send + Sync + 'static {
225    fn register_language(
226        &self,
227        config: LanguageConfig,
228        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
229    );
230
231    fn remove_languages(
232        &self,
233        languages_to_remove: &[LanguageName],
234        grammars_to_remove: &[Arc<str>],
235    );
236}
237
238impl ExtensionLanguageProxy for ExtensionHostProxy {
239    fn register_language(
240        &self,
241        language: LanguageConfig,
242        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
243    ) {
244        let Some(proxy) = self.language_proxy.read().clone() else {
245            return;
246        };
247
248        proxy.register_language(language, load)
249    }
250
251    fn remove_languages(
252        &self,
253        languages_to_remove: &[LanguageName],
254        grammars_to_remove: &[Arc<str>],
255    ) {
256        let Some(proxy) = self.language_proxy.read().clone() else {
257            return;
258        };
259
260        proxy.remove_languages(languages_to_remove, grammars_to_remove)
261    }
262}
263
264pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
265    fn register_language_server(
266        &self,
267        extension: Arc<dyn Extension>,
268        language_server_id: LanguageServerName,
269        language: LanguageName,
270    );
271
272    fn remove_language_server(
273        &self,
274        language: &LanguageName,
275        language_server_id: &LanguageServerName,
276    );
277
278    fn update_language_server_status(
279        &self,
280        language_server_id: LanguageServerName,
281        status: BinaryStatus,
282    );
283}
284
285impl ExtensionLanguageServerProxy for ExtensionHostProxy {
286    fn register_language_server(
287        &self,
288        extension: Arc<dyn Extension>,
289        language_server_id: LanguageServerName,
290        language: LanguageName,
291    ) {
292        let Some(proxy) = self.language_server_proxy.read().clone() else {
293            return;
294        };
295
296        proxy.register_language_server(extension, language_server_id, language)
297    }
298
299    fn remove_language_server(
300        &self,
301        language: &LanguageName,
302        language_server_id: &LanguageServerName,
303    ) {
304        let Some(proxy) = self.language_server_proxy.read().clone() else {
305            return;
306        };
307
308        proxy.remove_language_server(language, language_server_id)
309    }
310
311    fn update_language_server_status(
312        &self,
313        language_server_id: LanguageServerName,
314        status: BinaryStatus,
315    ) {
316        let Some(proxy) = self.language_server_proxy.read().clone() else {
317            return;
318        };
319
320        proxy.update_language_server_status(language_server_id, status)
321    }
322}
323
324pub trait ExtensionSnippetProxy: Send + Sync + 'static {
325    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
326}
327
328impl ExtensionSnippetProxy for ExtensionHostProxy {
329    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
330        let Some(proxy) = self.snippet_proxy.read().clone() else {
331            return Ok(());
332        };
333
334        proxy.register_snippet(path, snippet_contents)
335    }
336}
337
338pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
339    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
340}
341
342impl ExtensionSlashCommandProxy for ExtensionHostProxy {
343    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
344        let Some(proxy) = self.slash_command_proxy.read().clone() else {
345            return;
346        };
347
348        proxy.register_slash_command(extension, command)
349    }
350}
351
352pub trait ExtensionContextServerProxy: Send + Sync + 'static {
353    fn register_context_server(
354        &self,
355        extension: Arc<dyn Extension>,
356        server_id: Arc<str>,
357        cx: &mut App,
358    );
359
360    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
361}
362
363impl ExtensionContextServerProxy for ExtensionHostProxy {
364    fn register_context_server(
365        &self,
366        extension: Arc<dyn Extension>,
367        server_id: Arc<str>,
368        cx: &mut App,
369    ) {
370        let Some(proxy) = self.context_server_proxy.read().clone() else {
371            return;
372        };
373
374        proxy.register_context_server(extension, server_id, cx)
375    }
376
377    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
378        let Some(proxy) = self.context_server_proxy.read().clone() else {
379            return;
380        };
381
382        proxy.unregister_context_server(server_id, cx)
383    }
384}
385
386pub trait ExtensionIndexedDocsProviderProxy: Send + Sync + 'static {
387    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>);
388}
389
390impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
391    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
392        let Some(proxy) = self.indexed_docs_provider_proxy.read().clone() else {
393            return;
394        };
395
396        proxy.register_indexed_docs_provider(extension, provider_id)
397    }
398}