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