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