1use std::{path::PathBuf, sync::Arc};
2
3use anyhow::Result;
4use assistant_slash_command::SlashCommandRegistry;
5use context_servers::ContextServerFactoryRegistry;
6use extension_host::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host};
7use fs::Fs;
8use gpui::{AppContext, BackgroundExecutor, Task};
9use indexed_docs::{IndexedDocsRegistry, ProviderId};
10use language::{LanguageRegistry, LanguageServerBinaryStatus, LoadedLanguage};
11use snippet_provider::SnippetRegistry;
12use theme::{ThemeRegistry, ThemeSettings};
13use ui::SharedString;
14
15use crate::extension_context_server::ExtensionContextServer;
16use crate::{extension_indexed_docs_provider, extension_slash_command::ExtensionSlashCommand};
17
18pub struct ConcreteExtensionRegistrationHooks {
19 slash_command_registry: Arc<SlashCommandRegistry>,
20 theme_registry: Arc<ThemeRegistry>,
21 indexed_docs_registry: Arc<IndexedDocsRegistry>,
22 snippet_registry: Arc<SnippetRegistry>,
23 language_registry: Arc<LanguageRegistry>,
24 context_server_factory_registry: Arc<ContextServerFactoryRegistry>,
25 executor: BackgroundExecutor,
26}
27
28impl ConcreteExtensionRegistrationHooks {
29 pub fn new(
30 theme_registry: Arc<ThemeRegistry>,
31 slash_command_registry: Arc<SlashCommandRegistry>,
32 indexed_docs_registry: Arc<IndexedDocsRegistry>,
33 snippet_registry: Arc<SnippetRegistry>,
34 language_registry: Arc<LanguageRegistry>,
35 context_server_factory_registry: Arc<ContextServerFactoryRegistry>,
36 cx: &AppContext,
37 ) -> Arc<dyn extension_host::ExtensionRegistrationHooks> {
38 Arc::new(Self {
39 theme_registry,
40 slash_command_registry,
41 indexed_docs_registry,
42 snippet_registry,
43 language_registry,
44 context_server_factory_registry,
45 executor: cx.background_executor().clone(),
46 })
47 }
48}
49
50impl extension_host::ExtensionRegistrationHooks for ConcreteExtensionRegistrationHooks {
51 fn remove_user_themes(&self, themes: Vec<SharedString>) {
52 self.theme_registry.remove_user_themes(&themes);
53 }
54
55 fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn fs::Fs>) -> Task<Result<()>> {
56 let theme_registry = self.theme_registry.clone();
57 self.executor
58 .spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
59 }
60
61 fn register_slash_command(
62 &self,
63 command: wasm_host::SlashCommand,
64 extension: wasm_host::WasmExtension,
65 host: Arc<wasm_host::WasmHost>,
66 ) {
67 self.slash_command_registry.register_command(
68 ExtensionSlashCommand {
69 command,
70 extension,
71 host,
72 },
73 false,
74 )
75 }
76
77 fn register_context_server(
78 &self,
79 id: Arc<str>,
80 extension: wasm_host::WasmExtension,
81 host: Arc<wasm_host::WasmHost>,
82 ) {
83 self.context_server_factory_registry
84 .register_server_factory(
85 id.clone(),
86 Arc::new({
87 move |cx| {
88 let id = id.clone();
89 let extension = extension.clone();
90 let host = host.clone();
91 cx.spawn(|_cx| async move {
92 let context_server =
93 ExtensionContextServer::new(extension, host, id).await?;
94
95 anyhow::Ok(Arc::new(context_server) as _)
96 })
97 }
98 }),
99 );
100 }
101
102 fn register_docs_provider(
103 &self,
104 extension: wasm_host::WasmExtension,
105 host: Arc<wasm_host::WasmHost>,
106 provider_id: Arc<str>,
107 ) {
108 self.indexed_docs_registry.register_provider(Box::new(
109 extension_indexed_docs_provider::ExtensionIndexedDocsProvider {
110 extension,
111 host,
112 id: ProviderId(provider_id),
113 },
114 ));
115 }
116
117 fn register_snippets(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
118 self.snippet_registry
119 .register_snippets(path, snippet_contents)
120 }
121
122 fn update_lsp_status(
123 &self,
124 server_name: lsp::LanguageServerName,
125 status: LanguageServerBinaryStatus,
126 ) {
127 self.language_registry
128 .update_lsp_status(server_name, status);
129 }
130
131 fn register_lsp_adapter(
132 &self,
133 language_name: language::LanguageName,
134 adapter: ExtensionLspAdapter,
135 ) {
136 self.language_registry
137 .register_lsp_adapter(language_name, Arc::new(adapter));
138 }
139
140 fn remove_lsp_adapter(
141 &self,
142 language_name: &language::LanguageName,
143 server_name: &lsp::LanguageServerName,
144 ) {
145 self.language_registry
146 .remove_lsp_adapter(language_name, server_name);
147 }
148
149 fn remove_languages(
150 &self,
151 languages_to_remove: &[language::LanguageName],
152 grammars_to_remove: &[Arc<str>],
153 ) {
154 self.language_registry
155 .remove_languages(&languages_to_remove, &grammars_to_remove);
156 }
157
158 fn register_wasm_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
159 self.language_registry.register_wasm_grammars(grammars)
160 }
161
162 fn register_language(
163 &self,
164 language: language::LanguageName,
165 grammar: Option<Arc<str>>,
166 matcher: language::LanguageMatcher,
167 load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
168 ) {
169 self.language_registry
170 .register_language(language, grammar, matcher, load)
171 }
172
173 fn reload_current_theme(&self, cx: &mut AppContext) {
174 ThemeSettings::reload_current_theme(cx)
175 }
176
177 fn list_theme_names(&self, path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
178 self.executor.spawn(async move {
179 let themes = theme::read_user_theme(&path, fs).await?;
180 Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
181 })
182 }
183}