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}
 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        language: LanguageName,
228        grammar: Option<Arc<str>>,
229        matcher: LanguageMatcher,
230        hidden: bool,
231        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
232    );
233
234    fn remove_languages(
235        &self,
236        languages_to_remove: &[LanguageName],
237        grammars_to_remove: &[Arc<str>],
238    );
239}
240
241impl ExtensionLanguageProxy for ExtensionHostProxy {
242    fn register_language(
243        &self,
244        language: LanguageName,
245        grammar: Option<Arc<str>>,
246        matcher: LanguageMatcher,
247        hidden: bool,
248        load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
249    ) {
250        let Some(proxy) = self.language_proxy.read().clone() else {
251            return;
252        };
253
254        proxy.register_language(language, grammar, matcher, hidden, load)
255    }
256
257    fn remove_languages(
258        &self,
259        languages_to_remove: &[LanguageName],
260        grammars_to_remove: &[Arc<str>],
261    ) {
262        let Some(proxy) = self.language_proxy.read().clone() else {
263            return;
264        };
265
266        proxy.remove_languages(languages_to_remove, grammars_to_remove)
267    }
268}
269
270pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
271    fn register_language_server(
272        &self,
273        extension: Arc<dyn Extension>,
274        language_server_id: LanguageServerName,
275        language: LanguageName,
276    );
277
278    fn remove_language_server(
279        &self,
280        language: &LanguageName,
281        language_server_id: &LanguageServerName,
282    );
283
284    fn update_language_server_status(
285        &self,
286        language_server_id: LanguageServerName,
287        status: BinaryStatus,
288    );
289}
290
291impl ExtensionLanguageServerProxy for ExtensionHostProxy {
292    fn register_language_server(
293        &self,
294        extension: Arc<dyn Extension>,
295        language_server_id: LanguageServerName,
296        language: LanguageName,
297    ) {
298        let Some(proxy) = self.language_server_proxy.read().clone() else {
299            return;
300        };
301
302        proxy.register_language_server(extension, language_server_id, language)
303    }
304
305    fn remove_language_server(
306        &self,
307        language: &LanguageName,
308        language_server_id: &LanguageServerName,
309    ) {
310        let Some(proxy) = self.language_server_proxy.read().clone() else {
311            return;
312        };
313
314        proxy.remove_language_server(language, language_server_id)
315    }
316
317    fn update_language_server_status(
318        &self,
319        language_server_id: LanguageServerName,
320        status: BinaryStatus,
321    ) {
322        let Some(proxy) = self.language_server_proxy.read().clone() else {
323            return;
324        };
325
326        proxy.update_language_server_status(language_server_id, status)
327    }
328}
329
330pub trait ExtensionSnippetProxy: Send + Sync + 'static {
331    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
332}
333
334impl ExtensionSnippetProxy for ExtensionHostProxy {
335    fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
336        let Some(proxy) = self.snippet_proxy.read().clone() else {
337            return Ok(());
338        };
339
340        proxy.register_snippet(path, snippet_contents)
341    }
342}
343
344pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
345    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
346}
347
348impl ExtensionSlashCommandProxy for ExtensionHostProxy {
349    fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
350        let Some(proxy) = self.slash_command_proxy.read().clone() else {
351            return;
352        };
353
354        proxy.register_slash_command(extension, command)
355    }
356}
357
358pub trait ExtensionContextServerProxy: Send + Sync + 'static {
359    fn register_context_server(
360        &self,
361        extension: Arc<dyn Extension>,
362        server_id: Arc<str>,
363        cx: &mut App,
364    );
365
366    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
367}
368
369impl ExtensionContextServerProxy for ExtensionHostProxy {
370    fn register_context_server(
371        &self,
372        extension: Arc<dyn Extension>,
373        server_id: Arc<str>,
374        cx: &mut App,
375    ) {
376        let Some(proxy) = self.context_server_proxy.read().clone() else {
377            return;
378        };
379
380        proxy.register_context_server(extension, server_id, cx)
381    }
382
383    fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
384        let Some(proxy) = self.context_server_proxy.read().clone() else {
385            return;
386        };
387
388        proxy.unregister_context_server(server_id, cx)
389    }
390}
391
392pub trait ExtensionIndexedDocsProviderProxy: Send + Sync + 'static {
393    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>);
394}
395
396impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
397    fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
398        let Some(proxy) = self.indexed_docs_provider_proxy.read().clone() else {
399            return;
400        };
401
402        proxy.register_indexed_docs_provider(extension, provider_id)
403    }
404}