extension_host_proxy.rs

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