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;
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 context_server_proxy: RwLock<Option<Arc<dyn ExtensionContextServerProxy>>>,
33 debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
34 language_model_provider_proxy: RwLock<Option<Arc<dyn ExtensionLanguageModelProviderProxy>>>,
35}
36
37impl ExtensionHostProxy {
38 /// Returns the global [`ExtensionHostProxy`].
39 pub fn global(cx: &App) -> Arc<Self> {
40 GlobalExtensionHostProxy::global(cx).0.clone()
41 }
42
43 /// Returns the global [`ExtensionHostProxy`].
44 ///
45 /// Inserts a default [`ExtensionHostProxy`] if one does not yet exist.
46 pub fn default_global(cx: &mut App) -> Arc<Self> {
47 cx.default_global::<GlobalExtensionHostProxy>().0.clone()
48 }
49
50 pub fn new() -> Self {
51 Self {
52 theme_proxy: RwLock::default(),
53 grammar_proxy: RwLock::default(),
54 language_proxy: RwLock::default(),
55 language_server_proxy: RwLock::default(),
56 snippet_proxy: RwLock::default(),
57 context_server_proxy: RwLock::default(),
58 debug_adapter_provider_proxy: RwLock::default(),
59 language_model_provider_proxy: RwLock::default(),
60 }
61 }
62
63 pub fn register_theme_proxy(&self, proxy: impl ExtensionThemeProxy) {
64 self.theme_proxy.write().replace(Arc::new(proxy));
65 }
66
67 pub fn register_grammar_proxy(&self, proxy: impl ExtensionGrammarProxy) {
68 self.grammar_proxy.write().replace(Arc::new(proxy));
69 }
70
71 pub fn register_language_proxy(&self, proxy: impl ExtensionLanguageProxy) {
72 self.language_proxy.write().replace(Arc::new(proxy));
73 }
74
75 pub fn register_language_server_proxy(&self, proxy: impl ExtensionLanguageServerProxy) {
76 self.language_server_proxy.write().replace(Arc::new(proxy));
77 }
78
79 pub fn register_snippet_proxy(&self, proxy: impl ExtensionSnippetProxy) {
80 self.snippet_proxy.write().replace(Arc::new(proxy));
81 }
82
83 pub fn register_context_server_proxy(&self, proxy: impl ExtensionContextServerProxy) {
84 self.context_server_proxy.write().replace(Arc::new(proxy));
85 }
86
87 pub fn register_debug_adapter_proxy(&self, proxy: impl ExtensionDebugAdapterProviderProxy) {
88 self.debug_adapter_provider_proxy
89 .write()
90 .replace(Arc::new(proxy));
91 }
92
93 pub fn register_language_model_provider_proxy(
94 &self,
95 proxy: impl ExtensionLanguageModelProviderProxy,
96 ) {
97 self.language_model_provider_proxy
98 .write()
99 .replace(Arc::new(proxy));
100 }
101}
102
103pub trait ExtensionThemeProxy: Send + Sync + 'static {
104 fn set_extensions_loaded(&self);
105
106 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
107
108 fn remove_user_themes(&self, themes: Vec<SharedString>);
109
110 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
111
112 fn reload_current_theme(&self, cx: &mut App);
113
114 fn list_icon_theme_names(
115 &self,
116 icon_theme_path: PathBuf,
117 fs: Arc<dyn Fs>,
118 ) -> Task<Result<Vec<String>>>;
119
120 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
121
122 fn load_icon_theme(
123 &self,
124 icon_theme_path: PathBuf,
125 icons_root_dir: PathBuf,
126 fs: Arc<dyn Fs>,
127 ) -> Task<Result<()>>;
128
129 fn reload_current_icon_theme(&self, cx: &mut App);
130}
131
132impl ExtensionThemeProxy for ExtensionHostProxy {
133 fn set_extensions_loaded(&self) {
134 let Some(proxy) = self.theme_proxy.read().clone() else {
135 return;
136 };
137
138 proxy.set_extensions_loaded()
139 }
140
141 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
142 let Some(proxy) = self.theme_proxy.read().clone() else {
143 return Task::ready(Ok(Vec::new()));
144 };
145
146 proxy.list_theme_names(theme_path, fs)
147 }
148
149 fn remove_user_themes(&self, themes: Vec<SharedString>) {
150 let Some(proxy) = self.theme_proxy.read().clone() else {
151 return;
152 };
153
154 proxy.remove_user_themes(themes)
155 }
156
157 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
158 let Some(proxy) = self.theme_proxy.read().clone() else {
159 return Task::ready(Ok(()));
160 };
161
162 proxy.load_user_theme(theme_path, fs)
163 }
164
165 fn reload_current_theme(&self, cx: &mut App) {
166 let Some(proxy) = self.theme_proxy.read().clone() else {
167 return;
168 };
169
170 proxy.reload_current_theme(cx)
171 }
172
173 fn list_icon_theme_names(
174 &self,
175 icon_theme_path: PathBuf,
176 fs: Arc<dyn Fs>,
177 ) -> Task<Result<Vec<String>>> {
178 let Some(proxy) = self.theme_proxy.read().clone() else {
179 return Task::ready(Ok(Vec::new()));
180 };
181
182 proxy.list_icon_theme_names(icon_theme_path, fs)
183 }
184
185 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
186 let Some(proxy) = self.theme_proxy.read().clone() else {
187 return;
188 };
189
190 proxy.remove_icon_themes(icon_themes)
191 }
192
193 fn load_icon_theme(
194 &self,
195 icon_theme_path: PathBuf,
196 icons_root_dir: PathBuf,
197 fs: Arc<dyn Fs>,
198 ) -> Task<Result<()>> {
199 let Some(proxy) = self.theme_proxy.read().clone() else {
200 return Task::ready(Ok(()));
201 };
202
203 proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
204 }
205
206 fn reload_current_icon_theme(&self, cx: &mut App) {
207 let Some(proxy) = self.theme_proxy.read().clone() else {
208 return;
209 };
210
211 proxy.reload_current_icon_theme(cx)
212 }
213}
214
215pub trait ExtensionGrammarProxy: Send + Sync + 'static {
216 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
217}
218
219impl ExtensionGrammarProxy for ExtensionHostProxy {
220 #[ztracing::instrument(skip_all)]
221 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
222 let Some(proxy) = self.grammar_proxy.read().clone() else {
223 return;
224 };
225
226 proxy.register_grammars(grammars)
227 }
228}
229
230pub trait ExtensionLanguageProxy: Send + Sync + 'static {
231 fn register_language(
232 &self,
233 language: LanguageName,
234 grammar: Option<Arc<str>>,
235 matcher: LanguageMatcher,
236 hidden: bool,
237 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
238 );
239
240 fn remove_languages(
241 &self,
242 languages_to_remove: &[LanguageName],
243 grammars_to_remove: &[Arc<str>],
244 );
245}
246
247impl ExtensionLanguageProxy for ExtensionHostProxy {
248 #[ztracing::instrument(skip_all, fields(lang = language.0.as_str()))]
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 ExtensionContextServerProxy: Send + Sync + 'static {
354 fn register_context_server(
355 &self,
356 extension: Arc<dyn Extension>,
357 server_id: Arc<str>,
358 cx: &mut App,
359 );
360
361 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
362}
363
364impl ExtensionContextServerProxy for ExtensionHostProxy {
365 fn register_context_server(
366 &self,
367 extension: Arc<dyn Extension>,
368 server_id: Arc<str>,
369 cx: &mut App,
370 ) {
371 let Some(proxy) = self.context_server_proxy.read().clone() else {
372 return;
373 };
374
375 proxy.register_context_server(extension, server_id, cx)
376 }
377
378 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
379 let Some(proxy) = self.context_server_proxy.read().clone() else {
380 return;
381 };
382
383 proxy.unregister_context_server(server_id, cx)
384 }
385}
386
387pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
388 fn register_debug_adapter(
389 &self,
390 extension: Arc<dyn Extension>,
391 debug_adapter_name: Arc<str>,
392 schema_path: &Path,
393 );
394 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>);
395 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>);
396 fn unregister_debug_locator(&self, locator_name: Arc<str>);
397}
398
399impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
400 fn register_debug_adapter(
401 &self,
402 extension: Arc<dyn Extension>,
403 debug_adapter_name: Arc<str>,
404 schema_path: &Path,
405 ) {
406 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
407 return;
408 };
409
410 proxy.register_debug_adapter(extension, debug_adapter_name, schema_path)
411 }
412
413 fn register_debug_locator(&self, extension: Arc<dyn Extension>, locator_name: Arc<str>) {
414 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
415 return;
416 };
417
418 proxy.register_debug_locator(extension, locator_name)
419 }
420 fn unregister_debug_adapter(&self, debug_adapter_name: Arc<str>) {
421 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
422 return;
423 };
424
425 proxy.unregister_debug_adapter(debug_adapter_name)
426 }
427 fn unregister_debug_locator(&self, locator_name: Arc<str>) {
428 let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
429 return;
430 };
431
432 proxy.unregister_debug_locator(locator_name)
433 }
434}
435
436pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
437 fn register_language_model_provider(
438 &self,
439 provider_id: Arc<str>,
440 register_fn: LanguageModelProviderRegistration,
441 cx: &mut App,
442 );
443
444 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
445}
446
447impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
448 fn register_language_model_provider(
449 &self,
450 provider_id: Arc<str>,
451 register_fn: LanguageModelProviderRegistration,
452 cx: &mut App,
453 ) {
454 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
455 return;
456 };
457
458 proxy.register_language_model_provider(provider_id, register_fn, cx)
459 }
460
461 fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
462 let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
463 return;
464 };
465
466 proxy.unregister_language_model_provider(provider_id, cx)
467 }
468}