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