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 debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
32 language_model_provider_proxy: RwLock<Option<Arc<dyn ExtensionLanguageModelProviderProxy>>>,
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 debug_adapter_provider_proxy: RwLock::default(),
58 language_model_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_debug_adapter_proxy(&self, proxy: impl ExtensionDebugAdapterProviderProxy) {
91 self.debug_adapter_provider_proxy
92 .write()
93 .replace(Arc::new(proxy));
94 }
95
96 pub fn register_language_model_provider_proxy(
97 &self,
98 proxy: impl ExtensionLanguageModelProviderProxy,
99 ) {
100 self.language_model_provider_proxy
101 .write()
102 .replace(Arc::new(proxy));
103 }
104}
105
106pub trait ExtensionThemeProxy: Send + Sync + 'static {
107 fn set_extensions_loaded(&self);
108
109 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
110
111 fn remove_user_themes(&self, themes: Vec<SharedString>);
112
113 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
114
115 fn reload_current_theme(&self, cx: &mut App);
116
117 fn list_icon_theme_names(
118 &self,
119 icon_theme_path: PathBuf,
120 fs: Arc<dyn Fs>,
121 ) -> Task<Result<Vec<String>>>;
122
123 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
124
125 fn load_icon_theme(
126 &self,
127 icon_theme_path: PathBuf,
128 icons_root_dir: PathBuf,
129 fs: Arc<dyn Fs>,
130 ) -> Task<Result<()>>;
131
132 fn reload_current_icon_theme(&self, cx: &mut App);
133}
134
135impl ExtensionThemeProxy for ExtensionHostProxy {
136 fn set_extensions_loaded(&self) {
137 let Some(proxy) = self.theme_proxy.read().clone() else {
138 return;
139 };
140
141 proxy.set_extensions_loaded()
142 }
143
144 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
145 let Some(proxy) = self.theme_proxy.read().clone() else {
146 return Task::ready(Ok(Vec::new()));
147 };
148
149 proxy.list_theme_names(theme_path, fs)
150 }
151
152 fn remove_user_themes(&self, themes: Vec<SharedString>) {
153 let Some(proxy) = self.theme_proxy.read().clone() else {
154 return;
155 };
156
157 proxy.remove_user_themes(themes)
158 }
159
160 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
161 let Some(proxy) = self.theme_proxy.read().clone() else {
162 return Task::ready(Ok(()));
163 };
164
165 proxy.load_user_theme(theme_path, fs)
166 }
167
168 fn reload_current_theme(&self, cx: &mut App) {
169 let Some(proxy) = self.theme_proxy.read().clone() else {
170 return;
171 };
172
173 proxy.reload_current_theme(cx)
174 }
175
176 fn list_icon_theme_names(
177 &self,
178 icon_theme_path: PathBuf,
179 fs: Arc<dyn Fs>,
180 ) -> Task<Result<Vec<String>>> {
181 let Some(proxy) = self.theme_proxy.read().clone() else {
182 return Task::ready(Ok(Vec::new()));
183 };
184
185 proxy.list_icon_theme_names(icon_theme_path, fs)
186 }
187
188 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
189 let Some(proxy) = self.theme_proxy.read().clone() else {
190 return;
191 };
192
193 proxy.remove_icon_themes(icon_themes)
194 }
195
196 fn load_icon_theme(
197 &self,
198 icon_theme_path: PathBuf,
199 icons_root_dir: PathBuf,
200 fs: Arc<dyn Fs>,
201 ) -> Task<Result<()>> {
202 let Some(proxy) = self.theme_proxy.read().clone() else {
203 return Task::ready(Ok(()));
204 };
205
206 proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
207 }
208
209 fn reload_current_icon_theme(&self, cx: &mut App) {
210 let Some(proxy) = self.theme_proxy.read().clone() else {
211 return;
212 };
213
214 proxy.reload_current_icon_theme(cx)
215 }
216}
217
218pub trait ExtensionGrammarProxy: Send + Sync + 'static {
219 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
220}
221
222impl ExtensionGrammarProxy for ExtensionHostProxy {
223 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
224 let Some(proxy) = self.grammar_proxy.read().clone() else {
225 return;
226 };
227
228 proxy.register_grammars(grammars)
229 }
230}
231
232pub trait ExtensionLanguageProxy: Send + Sync + 'static {
233 fn register_language(
234 &self,
235 language: LanguageName,
236 grammar: Option<Arc<str>>,
237 matcher: LanguageMatcher,
238 hidden: bool,
239 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
240 );
241
242 fn remove_languages(
243 &self,
244 languages_to_remove: &[LanguageName],
245 grammars_to_remove: &[Arc<str>],
246 );
247}
248
249impl ExtensionLanguageProxy for ExtensionHostProxy {
250 fn register_language(
251 &self,
252 language: LanguageName,
253 grammar: Option<Arc<str>>,
254 matcher: LanguageMatcher,
255 hidden: bool,
256 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
257 ) {
258 let Some(proxy) = self.language_proxy.read().clone() else {
259 return;
260 };
261
262 proxy.register_language(language, grammar, matcher, hidden, load)
263 }
264
265 fn remove_languages(
266 &self,
267 languages_to_remove: &[LanguageName],
268 grammars_to_remove: &[Arc<str>],
269 ) {
270 let Some(proxy) = self.language_proxy.read().clone() else {
271 return;
272 };
273
274 proxy.remove_languages(languages_to_remove, grammars_to_remove)
275 }
276}
277
278pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
279 fn register_language_server(
280 &self,
281 extension: Arc<dyn Extension>,
282 language_server_id: LanguageServerName,
283 language: LanguageName,
284 );
285
286 fn remove_language_server(
287 &self,
288 language: &LanguageName,
289 language_server_id: &LanguageServerName,
290 cx: &mut App,
291 ) -> Task<Result<()>>;
292
293 fn update_language_server_status(
294 &self,
295 language_server_id: LanguageServerName,
296 status: BinaryStatus,
297 );
298}
299
300impl ExtensionLanguageServerProxy for ExtensionHostProxy {
301 fn register_language_server(
302 &self,
303 extension: Arc<dyn Extension>,
304 language_server_id: LanguageServerName,
305 language: LanguageName,
306 ) {
307 let Some(proxy) = self.language_server_proxy.read().clone() else {
308 return;
309 };
310
311 proxy.register_language_server(extension, language_server_id, language)
312 }
313
314 fn remove_language_server(
315 &self,
316 language: &LanguageName,
317 language_server_id: &LanguageServerName,
318 cx: &mut App,
319 ) -> Task<Result<()>> {
320 let Some(proxy) = self.language_server_proxy.read().clone() else {
321 return Task::ready(Ok(()));
322 };
323
324 proxy.remove_language_server(language, language_server_id, cx)
325 }
326
327 fn update_language_server_status(
328 &self,
329 language_server_id: LanguageServerName,
330 status: BinaryStatus,
331 ) {
332 let Some(proxy) = self.language_server_proxy.read().clone() else {
333 return;
334 };
335
336 proxy.update_language_server_status(language_server_id, status)
337 }
338}
339
340pub trait ExtensionSnippetProxy: Send + Sync + 'static {
341 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
342}
343
344impl ExtensionSnippetProxy for ExtensionHostProxy {
345 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
346 let Some(proxy) = self.snippet_proxy.read().clone() else {
347 return Ok(());
348 };
349
350 proxy.register_snippet(path, snippet_contents)
351 }
352}
353
354pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
355 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
356
357 fn unregister_slash_command(&self, command_name: Arc<str>);
358}
359
360impl ExtensionSlashCommandProxy for ExtensionHostProxy {
361 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
362 let Some(proxy) = self.slash_command_proxy.read().clone() else {
363 return;
364 };
365
366 proxy.register_slash_command(extension, command)
367 }
368
369 fn unregister_slash_command(&self, command_name: Arc<str>) {
370 let Some(proxy) = self.slash_command_proxy.read().clone() else {
371 return;
372 };
373
374 proxy.unregister_slash_command(command_name)
375 }
376}
377
378pub trait ExtensionContextServerProxy: Send + Sync + 'static {
379 fn register_context_server(
380 &self,
381 extension: Arc<dyn Extension>,
382 server_id: Arc<str>,
383 cx: &mut App,
384 );
385
386 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
387}
388
389/// A function that registers a language model provider with the registry.
390/// This allows extension_host to create the provider (which requires WasmExtension)
391/// and pass a registration closure to the language_models crate.
392pub type LanguageModelProviderRegistration = Box<dyn FnOnce(&mut App) + Send + Sync + 'static>;
393
394pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
395 /// Register an LLM provider from an extension.
396 /// The `register_fn` closure will be called with the App context and should
397 /// register the provider with the LanguageModelRegistry.
398 fn register_language_model_provider(
399 &self,
400 provider_id: Arc<str>,
401 register_fn: LanguageModelProviderRegistration,
402 cx: &mut App,
403 );
404
405 /// Unregister an LLM provider when an extension is unloaded.
406 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
407}
408
409impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
410 fn register_language_model_provider(
411 &self,
412 provider_id: Arc<str>,
413 register_fn: LanguageModelProviderRegistration,
414 cx: &mut App,
415 ) {
416 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
417 return;
418 };
419
420 proxy.register_language_model_provider(provider_id, register_fn, cx)
421 }
422
423 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
424 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
425 return;
426 };
427
428 proxy.unregister_language_model_provider(provider_id, cx)
429 }
430}
431
432impl ExtensionContextServerProxy for ExtensionHostProxy {
433 fn register_context_server(
434 &self,
435 extension: Arc<dyn Extension>,
436 server_id: Arc<str>,
437 cx: &mut App,
438 ) {
439 let Some(proxy) = self.context_server_proxy.read().clone() else {
440 return;
441 };
442
443 proxy.register_context_server(extension, server_id, cx)
444 }
445
446 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
447 let Some(proxy) = self.context_server_proxy.read().clone() else {
448 return;
449 };
450
451 proxy.unregister_context_server(server_id, cx)
452 }
453}
454
455pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
456 fn register_debug_adapter(
457 &self,
458 extension: Arc<dyn Extension>,
459 debug_adapter_name: Arc<str>,
460 schema_path: &Path,
461 );
462 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>);
463 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>);
464 fn unregister_debug_locator(&self, locator_name: Arc<str>);
465}
466
467impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
468 fn register_debug_adapter(
469 &self,
470 extension: Arc<dyn Extension>,
471 debug_adapter_name: Arc<str>,
472 schema_path: &Path,
473 ) {
474 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
475 return;
476 };
477
478 proxy.register_debug_adapter(extension, debug_adapter_name, schema_path)
479 }
480
481 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>) {
482 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
483 return;
484 };
485
486 proxy.register_debug_locator(extension, locator_name)
487 }
488 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>) {
489 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
490 return;
491 };
492
493 proxy.unregister_debug_adapter(debug_adapter_name)
494 }
495 fn unregister_debug_locator(&self, locator_name: Arc<str>) {
496 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
497 return;
498 };
499
500 proxy.unregister_debug_locator(locator_name)
501 }
502}