1use std::path::PathBuf;
2use std::sync::Arc;
3
4use anyhow::Result;
5use fs::Fs;
6use gpui::{App, Global, ReadGlobal, SharedString, Task};
7use language::{BinaryStatus, LanguageConfig, 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 indexed_docs_provider_proxy: RwLock<Option<Arc<dyn ExtensionIndexedDocsProviderProxy>>>,
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 indexed_docs_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_indexed_docs_provider_proxy(
89 &self,
90 proxy: impl ExtensionIndexedDocsProviderProxy,
91 ) {
92 self.indexed_docs_provider_proxy
93 .write()
94 .replace(Arc::new(proxy));
95 }
96}
97
98pub trait ExtensionThemeProxy: Send + Sync + 'static {
99 fn set_extensions_loaded(&self);
100
101 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
102
103 fn remove_user_themes(&self, themes: Vec<SharedString>);
104
105 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
106
107 fn reload_current_theme(&self, cx: &mut App);
108
109 fn list_icon_theme_names(
110 &self,
111 icon_theme_path: PathBuf,
112 fs: Arc<dyn Fs>,
113 ) -> Task<Result<Vec<String>>>;
114
115 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>);
116
117 fn load_icon_theme(
118 &self,
119 icon_theme_path: PathBuf,
120 icons_root_dir: PathBuf,
121 fs: Arc<dyn Fs>,
122 ) -> Task<Result<()>>;
123
124 fn reload_current_icon_theme(&self, cx: &mut App);
125}
126
127impl ExtensionThemeProxy for ExtensionHostProxy {
128 fn set_extensions_loaded(&self) {
129 let Some(proxy) = self.theme_proxy.read().clone() else {
130 return;
131 };
132
133 proxy.set_extensions_loaded()
134 }
135
136 fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
137 let Some(proxy) = self.theme_proxy.read().clone() else {
138 return Task::ready(Ok(Vec::new()));
139 };
140
141 proxy.list_theme_names(theme_path, fs)
142 }
143
144 fn remove_user_themes(&self, themes: Vec<SharedString>) {
145 let Some(proxy) = self.theme_proxy.read().clone() else {
146 return;
147 };
148
149 proxy.remove_user_themes(themes)
150 }
151
152 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
153 let Some(proxy) = self.theme_proxy.read().clone() else {
154 return Task::ready(Ok(()));
155 };
156
157 proxy.load_user_theme(theme_path, fs)
158 }
159
160 fn reload_current_theme(&self, cx: &mut App) {
161 let Some(proxy) = self.theme_proxy.read().clone() else {
162 return;
163 };
164
165 proxy.reload_current_theme(cx)
166 }
167
168 fn list_icon_theme_names(
169 &self,
170 icon_theme_path: PathBuf,
171 fs: Arc<dyn Fs>,
172 ) -> Task<Result<Vec<String>>> {
173 let Some(proxy) = self.theme_proxy.read().clone() else {
174 return Task::ready(Ok(Vec::new()));
175 };
176
177 proxy.list_icon_theme_names(icon_theme_path, fs)
178 }
179
180 fn remove_icon_themes(&self, icon_themes: Vec<SharedString>) {
181 let Some(proxy) = self.theme_proxy.read().clone() else {
182 return;
183 };
184
185 proxy.remove_icon_themes(icon_themes)
186 }
187
188 fn load_icon_theme(
189 &self,
190 icon_theme_path: PathBuf,
191 icons_root_dir: PathBuf,
192 fs: Arc<dyn Fs>,
193 ) -> Task<Result<()>> {
194 let Some(proxy) = self.theme_proxy.read().clone() else {
195 return Task::ready(Ok(()));
196 };
197
198 proxy.load_icon_theme(icon_theme_path, icons_root_dir, fs)
199 }
200
201 fn reload_current_icon_theme(&self, cx: &mut App) {
202 let Some(proxy) = self.theme_proxy.read().clone() else {
203 return;
204 };
205
206 proxy.reload_current_icon_theme(cx)
207 }
208}
209
210pub trait ExtensionGrammarProxy: Send + Sync + 'static {
211 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
212}
213
214impl ExtensionGrammarProxy for ExtensionHostProxy {
215 fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
216 let Some(proxy) = self.grammar_proxy.read().clone() else {
217 return;
218 };
219
220 proxy.register_grammars(grammars)
221 }
222}
223
224pub trait ExtensionLanguageProxy: Send + Sync + 'static {
225 fn register_language(
226 &self,
227 config: LanguageConfig,
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: LanguageConfig,
242 load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
243 ) {
244 let Some(proxy) = self.language_proxy.read().clone() else {
245 return;
246 };
247
248 proxy.register_language(language, load)
249 }
250
251 fn remove_languages(
252 &self,
253 languages_to_remove: &[LanguageName],
254 grammars_to_remove: &[Arc<str>],
255 ) {
256 let Some(proxy) = self.language_proxy.read().clone() else {
257 return;
258 };
259
260 proxy.remove_languages(languages_to_remove, grammars_to_remove)
261 }
262}
263
264pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
265 fn register_language_server(
266 &self,
267 extension: Arc<dyn Extension>,
268 language_server_id: LanguageServerName,
269 language: LanguageName,
270 );
271
272 fn remove_language_server(
273 &self,
274 language: &LanguageName,
275 language_server_id: &LanguageServerName,
276 );
277
278 fn update_language_server_status(
279 &self,
280 language_server_id: LanguageServerName,
281 status: BinaryStatus,
282 );
283}
284
285impl ExtensionLanguageServerProxy for ExtensionHostProxy {
286 fn register_language_server(
287 &self,
288 extension: Arc<dyn Extension>,
289 language_server_id: LanguageServerName,
290 language: LanguageName,
291 ) {
292 let Some(proxy) = self.language_server_proxy.read().clone() else {
293 return;
294 };
295
296 proxy.register_language_server(extension, language_server_id, language)
297 }
298
299 fn remove_language_server(
300 &self,
301 language: &LanguageName,
302 language_server_id: &LanguageServerName,
303 ) {
304 let Some(proxy) = self.language_server_proxy.read().clone() else {
305 return;
306 };
307
308 proxy.remove_language_server(language, language_server_id)
309 }
310
311 fn update_language_server_status(
312 &self,
313 language_server_id: LanguageServerName,
314 status: BinaryStatus,
315 ) {
316 let Some(proxy) = self.language_server_proxy.read().clone() else {
317 return;
318 };
319
320 proxy.update_language_server_status(language_server_id, status)
321 }
322}
323
324pub trait ExtensionSnippetProxy: Send + Sync + 'static {
325 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
326}
327
328impl ExtensionSnippetProxy for ExtensionHostProxy {
329 fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
330 let Some(proxy) = self.snippet_proxy.read().clone() else {
331 return Ok(());
332 };
333
334 proxy.register_snippet(path, snippet_contents)
335 }
336}
337
338pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
339 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
340}
341
342impl ExtensionSlashCommandProxy for ExtensionHostProxy {
343 fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
344 let Some(proxy) = self.slash_command_proxy.read().clone() else {
345 return;
346 };
347
348 proxy.register_slash_command(extension, command)
349 }
350}
351
352pub trait ExtensionContextServerProxy: Send + Sync + 'static {
353 fn register_context_server(
354 &self,
355 extension: Arc<dyn Extension>,
356 server_id: Arc<str>,
357 cx: &mut App,
358 );
359
360 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
361}
362
363impl ExtensionContextServerProxy for ExtensionHostProxy {
364 fn register_context_server(
365 &self,
366 extension: Arc<dyn Extension>,
367 server_id: Arc<str>,
368 cx: &mut App,
369 ) {
370 let Some(proxy) = self.context_server_proxy.read().clone() else {
371 return;
372 };
373
374 proxy.register_context_server(extension, server_id, cx)
375 }
376
377 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
378 let Some(proxy) = self.context_server_proxy.read().clone() else {
379 return;
380 };
381
382 proxy.unregister_context_server(server_id, cx)
383 }
384}
385
386pub trait ExtensionIndexedDocsProviderProxy: Send + Sync + 'static {
387 fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>);
388}
389
390impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
391 fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
392 let Some(proxy) = self.indexed_docs_provider_proxy.read().clone() else {
393 return;
394 };
395
396 proxy.register_indexed_docs_provider(extension, provider_id)
397 }
398}