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