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