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