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