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 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
227 let Some(proxy) = self.grammar_proxy.read().clone() else {
228 return;
229 };
230
231 proxy.register_grammars(grammars)
232 }
233}
234
235pub trait ExtensionLanguageProxy: Send + Sync + 'static {
236 fn register_language(
237 &self,
238 language: LanguageName,
239 grammar: Option<Arc<str>>,
240 matcher: LanguageMatcher,
241 hidden: bool,
242 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
243 );
244
245 fn remove_languages(
246 &self,
247 languages_to_remove: &[LanguageName],
248 grammars_to_remove: &[Arc<str>],
249 );
250}
251
252impl ExtensionLanguageProxy for ExtensionHostProxy {
253 fn register_language(
254 &self,
255 language: LanguageName,
256 grammar: Option<Arc<str>>,
257 matcher: LanguageMatcher,
258 hidden: bool,
259 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
260 ) {
261 let Some(proxy) = self.language_proxy.read().clone() else {
262 return;
263 };
264
265 proxy.register_language(language, grammar, matcher, hidden, load)
266 }
267
268 fn remove_languages(
269 &self,
270 languages_to_remove: &[LanguageName],
271 grammars_to_remove: &[Arc<str>],
272 ) {
273 let Some(proxy) = self.language_proxy.read().clone() else {
274 return;
275 };
276
277 proxy.remove_languages(languages_to_remove, grammars_to_remove)
278 }
279}
280
281pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
282 fn register_language_server(
283 &self,
284 extension: Arc<dyn Extension>,
285 language_server_id: LanguageServerName,
286 language: LanguageName,
287 );
288
289 fn remove_language_server(
290 &self,
291 language: &LanguageName,
292 language_server_id: &LanguageServerName,
293 cx: &mut App,
294 ) -> Task<Result<()>>;
295
296 fn update_language_server_status(
297 &self,
298 language_server_id: LanguageServerName,
299 status: BinaryStatus,
300 );
301}
302
303impl ExtensionLanguageServerProxy for ExtensionHostProxy {
304 fn register_language_server(
305 &self,
306 extension: Arc<dyn Extension>,
307 language_server_id: LanguageServerName,
308 language: LanguageName,
309 ) {
310 let Some(proxy) = self.language_server_proxy.read().clone() else {
311 return;
312 };
313
314 proxy.register_language_server(extension, language_server_id, language)
315 }
316
317 fn remove_language_server(
318 &self,
319 language: &LanguageName,
320 language_server_id: &LanguageServerName,
321 cx: &mut App,
322 ) -> Task<Result<()>> {
323 let Some(proxy) = self.language_server_proxy.read().clone() else {
324 return Task::ready(Ok(()));
325 };
326
327 proxy.remove_language_server(language, language_server_id, cx)
328 }
329
330 fn update_language_server_status(
331 &self,
332 language_server_id: LanguageServerName,
333 status: BinaryStatus,
334 ) {
335 let Some(proxy) = self.language_server_proxy.read().clone() else {
336 return;
337 };
338
339 proxy.update_language_server_status(language_server_id, status)
340 }
341}
342
343pub trait ExtensionSnippetProxy: Send + Sync + 'static {
344 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
345}
346
347impl ExtensionSnippetProxy for ExtensionHostProxy {
348 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
349 let Some(proxy) = self.snippet_proxy.read().clone() else {
350 return Ok(());
351 };
352
353 proxy.register_snippet(path, snippet_contents)
354 }
355}
356
357pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
358 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
359
360 fn unregister_slash_command(&self, command_name: Arc<str>);
361}
362
363impl ExtensionSlashCommandProxy for ExtensionHostProxy {
364 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
365 let Some(proxy) = self.slash_command_proxy.read().clone() else {
366 return;
367 };
368
369 proxy.register_slash_command(extension, command)
370 }
371
372 fn unregister_slash_command(&self, command_name: Arc<str>) {
373 let Some(proxy) = self.slash_command_proxy.read().clone() else {
374 return;
375 };
376
377 proxy.unregister_slash_command(command_name)
378 }
379}
380
381pub trait ExtensionContextServerProxy: Send + Sync + 'static {
382 fn register_context_server(
383 &self,
384 extension: Arc<dyn Extension>,
385 server_id: Arc<str>,
386 cx: &mut App,
387 );
388
389 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
390}
391
392/// A function that registers a language model provider with the registry.
393/// This allows extension_host to create the provider (which requires WasmExtension)
394/// and pass a registration closure to the language_models crate.
395pub type LanguageModelProviderRegistration = Box<dyn FnOnce(&mut App) + Send + Sync + 'static>;
396
397pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
398 /// Register an LLM provider from an extension.
399 /// The `register_fn` closure will be called with the App context and should
400 /// register the provider with the LanguageModelRegistry.
401 fn register_language_model_provider(
402 &self,
403 provider_id: Arc<str>,
404 register_fn: LanguageModelProviderRegistration,
405 cx: &mut App,
406 );
407
408 /// Unregister an LLM provider when an extension is unloaded.
409 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
410}
411
412impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
413 fn register_language_model_provider(
414 &self,
415 provider_id: Arc<str>,
416 register_fn: LanguageModelProviderRegistration,
417 cx: &mut App,
418 ) {
419 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
420 return;
421 };
422
423 proxy.register_language_model_provider(provider_id, register_fn, cx)
424 }
425
426 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
427 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
428 return;
429 };
430
431 proxy.unregister_language_model_provider(provider_id, cx)
432 }
433}
434
435impl ExtensionContextServerProxy for ExtensionHostProxy {
436 fn register_context_server(
437 &self,
438 extension: Arc<dyn Extension>,
439 server_id: Arc<str>,
440 cx: &mut App,
441 ) {
442 let Some(proxy) = self.context_server_proxy.read().clone() else {
443 return;
444 };
445
446 proxy.register_context_server(extension, server_id, cx)
447 }
448
449 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
450 let Some(proxy) = self.context_server_proxy.read().clone() else {
451 return;
452 };
453
454 proxy.unregister_context_server(server_id, cx)
455 }
456}
457
458pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
459 fn register_debug_adapter(
460 &self,
461 extension: Arc<dyn Extension>,
462 debug_adapter_name: Arc<str>,
463 schema_path: &Path,
464 );
465 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>);
466 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>);
467 fn unregister_debug_locator(&self, locator_name: Arc<str>);
468}
469
470impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
471 fn register_debug_adapter(
472 &self,
473 extension: Arc<dyn Extension>,
474 debug_adapter_name: Arc<str>,
475 schema_path: &Path,
476 ) {
477 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
478 return;
479 };
480
481 proxy.register_debug_adapter(extension, debug_adapter_name, schema_path)
482 }
483
484 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>) {
485 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
486 return;
487 };
488
489 proxy.register_debug_locator(extension, locator_name)
490 }
491 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>) {
492 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
493 return;
494 };
495
496 proxy.unregister_debug_adapter(debug_adapter_name)
497 }
498 fn unregister_debug_locator(&self, locator_name: Arc<str>) {
499 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
500 return;
501 };
502
503 proxy.unregister_debug_locator(locator_name)
504 }
505}
506
507pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
508 fn register_language_model_provider(
509 &self,
510 provider_id: Arc<str>,
511 register_fn: LanguageModelProviderRegistration,
512 cx: &mut App,
513 );
514
515 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
516}
517
518impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
519 fn register_language_model_provider(
520 &self,
521 provider_id: Arc<str>,
522 register_fn: LanguageModelProviderRegistration,
523 cx: &mut App,
524 ) {
525 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
526 return;
527 };
528
529 proxy.register_language_model_provider(provider_id, register_fn, cx)
530 }
531
532 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
533 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
534 return;
535 };
536
537 proxy.unregister_language_model_provider(provider_id, cx)
538 }
539}