1pub mod extension_builder;
2mod extension_lsp_adapter;
3mod extension_manifest;
4mod wasm_host;
5
6#[cfg(test)]
7mod extension_store_test;
8
9use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
10use anyhow::{anyhow, bail, Context as _, Result};
11use async_compression::futures::bufread::GzipDecoder;
12use async_tar::Archive;
13use collections::{hash_map, BTreeMap, HashMap, HashSet};
14use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
15use fs::{Fs, RemoveOptions};
16use futures::{
17 channel::{
18 mpsc::{unbounded, UnboundedSender},
19 oneshot,
20 },
21 io::BufReader,
22 select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
23};
24use gpui::{actions, AppContext, Context, EventEmitter, Global, Model, ModelContext, Task};
25use language::{
26 LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
27};
28use node_runtime::NodeRuntime;
29use serde::{Deserialize, Serialize};
30use std::{
31 cmp::Ordering,
32 ffi::OsStr,
33 path::{self, Path, PathBuf},
34 sync::Arc,
35 time::{Duration, Instant},
36};
37use theme::{ThemeRegistry, ThemeSettings};
38use url::Url;
39use util::{
40 http::{AsyncBody, HttpClient, HttpClientWithUrl},
41 paths::EXTENSIONS_DIR,
42 ResultExt,
43};
44use wasm_host::{WasmExtension, WasmHost};
45
46pub use extension_manifest::{
47 ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
48};
49
50const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
51const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
52
53const CURRENT_SCHEMA_VERSION: i64 = 1;
54
55#[derive(Deserialize)]
56pub struct ExtensionsApiResponse {
57 pub data: Vec<ExtensionApiResponse>,
58}
59
60#[derive(Clone, Deserialize)]
61pub struct ExtensionApiResponse {
62 pub id: Arc<str>,
63 pub name: String,
64 pub version: Arc<str>,
65 pub description: Option<String>,
66 pub authors: Vec<String>,
67 pub repository: String,
68 pub download_count: usize,
69}
70
71pub struct ExtensionStore {
72 builder: Arc<ExtensionBuilder>,
73 extension_index: ExtensionIndex,
74 fs: Arc<dyn Fs>,
75 http_client: Arc<HttpClientWithUrl>,
76 reload_tx: UnboundedSender<Option<Arc<str>>>,
77 reload_complete_senders: Vec<oneshot::Sender<()>>,
78 installed_dir: PathBuf,
79 outstanding_operations: HashMap<Arc<str>, ExtensionOperation>,
80 index_path: PathBuf,
81 language_registry: Arc<LanguageRegistry>,
82 theme_registry: Arc<ThemeRegistry>,
83 modified_extensions: HashSet<Arc<str>>,
84 wasm_host: Arc<WasmHost>,
85 wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
86 tasks: Vec<Task<()>>,
87}
88
89#[derive(Clone)]
90pub enum ExtensionStatus {
91 NotInstalled,
92 Installing,
93 Upgrading,
94 Installed(Arc<str>),
95 Removing,
96}
97
98#[derive(Clone, Copy)]
99enum ExtensionOperation {
100 Upgrade,
101 Install,
102 Remove,
103}
104
105#[derive(Clone)]
106pub enum Event {
107 ExtensionsUpdated,
108 StartedReloading,
109 ExtensionInstalled(Arc<str>),
110}
111
112impl EventEmitter<Event> for ExtensionStore {}
113
114struct GlobalExtensionStore(Model<ExtensionStore>);
115
116impl Global for GlobalExtensionStore {}
117
118#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
119pub struct ExtensionIndex {
120 pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
121 pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
122 pub languages: BTreeMap<Arc<str>, ExtensionIndexLanguageEntry>,
123}
124
125#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
126pub struct ExtensionIndexEntry {
127 manifest: Arc<ExtensionManifest>,
128 dev: bool,
129}
130
131#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
132pub struct ExtensionIndexThemeEntry {
133 extension: Arc<str>,
134 path: PathBuf,
135}
136
137#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
138pub struct ExtensionIndexLanguageEntry {
139 extension: Arc<str>,
140 path: PathBuf,
141 matcher: LanguageMatcher,
142 grammar: Option<Arc<str>>,
143}
144
145actions!(zed, [ReloadExtensions]);
146
147pub fn init(
148 fs: Arc<fs::RealFs>,
149 http_client: Arc<HttpClientWithUrl>,
150 node_runtime: Arc<dyn NodeRuntime>,
151 language_registry: Arc<LanguageRegistry>,
152 theme_registry: Arc<ThemeRegistry>,
153 cx: &mut AppContext,
154) {
155 let store = cx.new_model(move |cx| {
156 ExtensionStore::new(
157 EXTENSIONS_DIR.clone(),
158 None,
159 fs,
160 http_client,
161 node_runtime,
162 language_registry,
163 theme_registry,
164 cx,
165 )
166 });
167
168 cx.on_action(|_: &ReloadExtensions, cx| {
169 let store = cx.global::<GlobalExtensionStore>().0.clone();
170 store.update(cx, |store, cx| drop(store.reload(None, cx)));
171 });
172
173 cx.set_global(GlobalExtensionStore(store));
174}
175
176impl ExtensionStore {
177 pub fn global(cx: &AppContext) -> Model<Self> {
178 cx.global::<GlobalExtensionStore>().0.clone()
179 }
180
181 #[allow(clippy::too_many_arguments)]
182 pub fn new(
183 extensions_dir: PathBuf,
184 build_dir: Option<PathBuf>,
185 fs: Arc<dyn Fs>,
186 http_client: Arc<HttpClientWithUrl>,
187 node_runtime: Arc<dyn NodeRuntime>,
188 language_registry: Arc<LanguageRegistry>,
189 theme_registry: Arc<ThemeRegistry>,
190 cx: &mut ModelContext<Self>,
191 ) -> Self {
192 let work_dir = extensions_dir.join("work");
193 let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
194 let installed_dir = extensions_dir.join("installed");
195 let index_path = extensions_dir.join("index.json");
196
197 let (reload_tx, mut reload_rx) = unbounded();
198 let mut this = Self {
199 extension_index: Default::default(),
200 installed_dir,
201 index_path,
202 builder: Arc::new(ExtensionBuilder::new(build_dir)),
203 outstanding_operations: Default::default(),
204 modified_extensions: Default::default(),
205 reload_complete_senders: Vec::new(),
206 wasm_host: WasmHost::new(
207 fs.clone(),
208 http_client.clone(),
209 node_runtime,
210 language_registry.clone(),
211 work_dir,
212 ),
213 wasm_extensions: Vec::new(),
214 fs,
215 http_client,
216 language_registry,
217 theme_registry,
218 reload_tx,
219 tasks: Vec::new(),
220 };
221
222 // The extensions store maintains an index file, which contains a complete
223 // list of the installed extensions and the resources that they provide.
224 // This index is loaded synchronously on startup.
225 let (index_content, index_metadata, extensions_metadata) =
226 cx.background_executor().block(async {
227 futures::join!(
228 this.fs.load(&this.index_path),
229 this.fs.metadata(&this.index_path),
230 this.fs.metadata(&this.installed_dir),
231 )
232 });
233
234 // Normally, there is no need to rebuild the index. But if the index file
235 // is invalid or is out-of-date according to the filesystem mtimes, then
236 // it must be asynchronously rebuilt.
237 let mut extension_index = ExtensionIndex::default();
238 let mut extension_index_needs_rebuild = true;
239 if let Some(index_content) = index_content.ok() {
240 if let Some(index) = serde_json::from_str(&index_content).log_err() {
241 extension_index = index;
242 if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
243 (index_metadata, extensions_metadata)
244 {
245 if index_metadata.mtime > extensions_metadata.mtime {
246 extension_index_needs_rebuild = false;
247 }
248 }
249 }
250 }
251
252 // Immediately load all of the extensions in the initial manifest. If the
253 // index needs to be rebuild, then enqueue
254 let load_initial_extensions = this.extensions_updated(extension_index, cx);
255 if extension_index_needs_rebuild {
256 let _ = this.reload(None, cx);
257 }
258
259 // Perform all extension loading in a single task to ensure that we
260 // never attempt to simultaneously load/unload extensions from multiple
261 // parallel tasks.
262 this.tasks.push(cx.spawn(|this, mut cx| {
263 async move {
264 load_initial_extensions.await;
265
266 let mut debounce_timer = cx
267 .background_executor()
268 .spawn(futures::future::pending())
269 .fuse();
270 loop {
271 select_biased! {
272 _ = debounce_timer => {
273 let index = this
274 .update(&mut cx, |this, cx| this.rebuild_extension_index(cx))?
275 .await;
276 this.update(&mut cx, |this, cx| this.extensions_updated(index, cx))?
277 .await;
278 }
279 extension_id = reload_rx.next() => {
280 let Some(extension_id) = extension_id else { break; };
281 this.update(&mut cx, |this, _| {
282 this.modified_extensions.extend(extension_id);
283 })?;
284 debounce_timer = cx
285 .background_executor()
286 .timer(RELOAD_DEBOUNCE_DURATION)
287 .fuse();
288 }
289 }
290 }
291
292 anyhow::Ok(())
293 }
294 .map(drop)
295 }));
296
297 // Watch the installed extensions directory for changes. Whenever changes are
298 // detected, rebuild the extension index, and load/unload any extensions that
299 // have been added, removed, or modified.
300 this.tasks.push(cx.background_executor().spawn({
301 let fs = this.fs.clone();
302 let reload_tx = this.reload_tx.clone();
303 let installed_dir = this.installed_dir.clone();
304 async move {
305 let mut paths = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
306 while let Some(paths) = paths.next().await {
307 for path in paths {
308 let Ok(event_path) = path.strip_prefix(&installed_dir) else {
309 continue;
310 };
311
312 if let Some(path::Component::Normal(extension_dir_name)) =
313 event_path.components().next()
314 {
315 if let Some(extension_id) = extension_dir_name.to_str() {
316 reload_tx.unbounded_send(Some(extension_id.into())).ok();
317 }
318 }
319 }
320 }
321 }
322 }));
323
324 this
325 }
326
327 fn reload(
328 &mut self,
329 modified_extension: Option<Arc<str>>,
330 cx: &mut ModelContext<Self>,
331 ) -> impl Future<Output = ()> {
332 let (tx, rx) = oneshot::channel();
333 self.reload_complete_senders.push(tx);
334 self.reload_tx
335 .unbounded_send(modified_extension)
336 .expect("reload task exited");
337 cx.emit(Event::StartedReloading);
338
339 async move {
340 rx.await.ok();
341 }
342 }
343
344 fn extensions_dir(&self) -> PathBuf {
345 self.installed_dir.clone()
346 }
347
348 pub fn extension_status(&self, extension_id: &str) -> ExtensionStatus {
349 match self.outstanding_operations.get(extension_id) {
350 Some(ExtensionOperation::Install) => ExtensionStatus::Installing,
351 Some(ExtensionOperation::Remove) => ExtensionStatus::Removing,
352 Some(ExtensionOperation::Upgrade) => ExtensionStatus::Upgrading,
353 None => match self.extension_index.extensions.get(extension_id) {
354 Some(extension) => ExtensionStatus::Installed(extension.manifest.version.clone()),
355 None => ExtensionStatus::NotInstalled,
356 },
357 }
358 }
359
360 pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
361 self.extension_index
362 .extensions
363 .values()
364 .filter_map(|extension| extension.dev.then_some(&extension.manifest))
365 }
366
367 /// Returns the names of themes provided by extensions.
368 pub fn extension_themes<'a>(
369 &'a self,
370 extension_id: &'a str,
371 ) -> impl Iterator<Item = &'a Arc<str>> {
372 self.extension_index
373 .themes
374 .iter()
375 .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
376 }
377
378 pub fn fetch_extensions(
379 &self,
380 search: Option<&str>,
381 cx: &mut ModelContext<Self>,
382 ) -> Task<Result<Vec<ExtensionApiResponse>>> {
383 let version = CURRENT_SCHEMA_VERSION.to_string();
384 let mut query = vec![("max_schema_version", version.as_str())];
385 if let Some(search) = search {
386 query.push(("filter", search));
387 }
388
389 let url = self.http_client.build_zed_api_url("/extensions", &query);
390 let http_client = self.http_client.clone();
391 cx.spawn(move |_, _| async move {
392 let mut response = http_client
393 .get(&url?.as_ref(), AsyncBody::empty(), true)
394 .await?;
395
396 let mut body = Vec::new();
397 response
398 .body_mut()
399 .read_to_end(&mut body)
400 .await
401 .context("error reading extensions")?;
402
403 if response.status().is_client_error() {
404 let text = String::from_utf8_lossy(body.as_slice());
405 bail!(
406 "status error {}, response: {text:?}",
407 response.status().as_u16()
408 );
409 }
410
411 let response: ExtensionsApiResponse = serde_json::from_slice(&body)?;
412
413 Ok(response.data)
414 })
415 }
416
417 pub fn install_extension(
418 &mut self,
419 extension_id: Arc<str>,
420 version: Arc<str>,
421 cx: &mut ModelContext<Self>,
422 ) {
423 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
424 }
425
426 fn install_or_upgrade_extension_at_endpoint(
427 &mut self,
428 extension_id: Arc<str>,
429 url: Url,
430 operation: ExtensionOperation,
431 cx: &mut ModelContext<Self>,
432 ) {
433 let extensions_dir = self.extensions_dir();
434 let http_client = self.http_client.clone();
435
436 match self.outstanding_operations.entry(extension_id.clone()) {
437 hash_map::Entry::Occupied(_) => return,
438 hash_map::Entry::Vacant(e) => e.insert(operation),
439 };
440
441 cx.spawn(move |this, mut cx| async move {
442 let _finish = util::defer({
443 let this = this.clone();
444 let mut cx = cx.clone();
445 let extension_id = extension_id.clone();
446 move || {
447 this.update(&mut cx, |this, cx| {
448 this.outstanding_operations.remove(extension_id.as_ref());
449 cx.notify();
450 })
451 .ok();
452 }
453 });
454
455 let mut response = http_client
456 .get(&url.as_ref(), Default::default(), true)
457 .await
458 .map_err(|err| anyhow!("error downloading extension: {}", err))?;
459 let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
460 let archive = Archive::new(decompressed_bytes);
461 archive
462 .unpack(extensions_dir.join(extension_id.as_ref()))
463 .await?;
464 this.update(&mut cx, |this, cx| {
465 this.reload(Some(extension_id.clone()), cx)
466 })?
467 .await;
468
469 match operation {
470 ExtensionOperation::Install => {
471 this.update(&mut cx, |_, cx| {
472 cx.emit(Event::ExtensionInstalled(extension_id));
473 })
474 .ok();
475 }
476 _ => {}
477 }
478
479 anyhow::Ok(())
480 })
481 .detach_and_log_err(cx);
482 }
483
484 pub fn install_latest_extension(
485 &mut self,
486 extension_id: Arc<str>,
487 cx: &mut ModelContext<Self>,
488 ) {
489 log::info!("installing extension {extension_id} latest version");
490
491 let Some(url) = self
492 .http_client
493 .build_zed_api_url(&format!("/extensions/{extension_id}/download"), &[])
494 .log_err()
495 else {
496 return;
497 };
498
499 self.install_or_upgrade_extension_at_endpoint(
500 extension_id,
501 url,
502 ExtensionOperation::Install,
503 cx,
504 );
505 }
506
507 pub fn upgrade_extension(
508 &mut self,
509 extension_id: Arc<str>,
510 version: Arc<str>,
511 cx: &mut ModelContext<Self>,
512 ) {
513 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
514 }
515
516 fn install_or_upgrade_extension(
517 &mut self,
518 extension_id: Arc<str>,
519 version: Arc<str>,
520 operation: ExtensionOperation,
521 cx: &mut ModelContext<Self>,
522 ) {
523 log::info!("installing extension {extension_id} {version}");
524 let Some(url) = self
525 .http_client
526 .build_zed_api_url(
527 &format!("/extensions/{extension_id}/{version}/download"),
528 &[],
529 )
530 .log_err()
531 else {
532 return;
533 };
534
535 self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx);
536 }
537
538 pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
539 let extensions_dir = self.extensions_dir();
540 let fs = self.fs.clone();
541
542 match self.outstanding_operations.entry(extension_id.clone()) {
543 hash_map::Entry::Occupied(_) => return,
544 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
545 };
546
547 cx.spawn(move |this, mut cx| async move {
548 let _finish = util::defer({
549 let this = this.clone();
550 let mut cx = cx.clone();
551 let extension_id = extension_id.clone();
552 move || {
553 this.update(&mut cx, |this, cx| {
554 this.outstanding_operations.remove(extension_id.as_ref());
555 cx.notify();
556 })
557 .ok();
558 }
559 });
560
561 fs.remove_dir(
562 &extensions_dir.join(extension_id.as_ref()),
563 RemoveOptions {
564 recursive: true,
565 ignore_if_not_exists: true,
566 },
567 )
568 .await?;
569
570 this.update(&mut cx, |this, cx| this.reload(None, cx))?
571 .await;
572 anyhow::Ok(())
573 })
574 .detach_and_log_err(cx)
575 }
576
577 pub fn install_dev_extension(
578 &mut self,
579 extension_source_path: PathBuf,
580 cx: &mut ModelContext<Self>,
581 ) -> Task<Result<()>> {
582 let extensions_dir = self.extensions_dir();
583 let fs = self.fs.clone();
584 let builder = self.builder.clone();
585
586 cx.spawn(move |this, mut cx| async move {
587 let extension_manifest =
588 Self::load_extension_manifest(fs.clone(), &extension_source_path).await?;
589 let extension_id = extension_manifest.id.clone();
590
591 if !this.update(&mut cx, |this, cx| {
592 match this.outstanding_operations.entry(extension_id.clone()) {
593 hash_map::Entry::Occupied(_) => return false,
594 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
595 };
596 cx.notify();
597 true
598 })? {
599 return Ok(());
600 }
601
602 let _finish = util::defer({
603 let this = this.clone();
604 let mut cx = cx.clone();
605 let extension_id = extension_id.clone();
606 move || {
607 this.update(&mut cx, |this, cx| {
608 this.outstanding_operations.remove(extension_id.as_ref());
609 cx.notify();
610 })
611 .ok();
612 }
613 });
614
615 cx.background_executor()
616 .spawn({
617 let extension_source_path = extension_source_path.clone();
618 async move {
619 builder
620 .compile_extension(
621 &extension_source_path,
622 &extension_manifest,
623 CompileExtensionOptions { release: false },
624 )
625 .await
626 }
627 })
628 .await?;
629
630 let output_path = &extensions_dir.join(extension_id.as_ref());
631 if let Some(metadata) = fs.metadata(&output_path).await? {
632 if metadata.is_symlink {
633 fs.remove_file(
634 &output_path,
635 RemoveOptions {
636 recursive: false,
637 ignore_if_not_exists: true,
638 },
639 )
640 .await?;
641 } else {
642 bail!("extension {extension_id} is already installed");
643 }
644 }
645
646 fs.create_symlink(output_path, extension_source_path)
647 .await?;
648
649 this.update(&mut cx, |this, cx| this.reload(None, cx))?
650 .await;
651 Ok(())
652 })
653 }
654
655 pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
656 let path = self.installed_dir.join(extension_id.as_ref());
657 let builder = self.builder.clone();
658 let fs = self.fs.clone();
659
660 match self.outstanding_operations.entry(extension_id.clone()) {
661 hash_map::Entry::Occupied(_) => return,
662 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
663 };
664
665 cx.notify();
666 let compile = cx.background_executor().spawn(async move {
667 let manifest = Self::load_extension_manifest(fs, &path).await?;
668 builder
669 .compile_extension(&path, &manifest, CompileExtensionOptions { release: true })
670 .await
671 });
672
673 cx.spawn(|this, mut cx| async move {
674 let result = compile.await;
675
676 this.update(&mut cx, |this, cx| {
677 this.outstanding_operations.remove(&extension_id);
678 cx.notify();
679 })?;
680
681 if result.is_ok() {
682 this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
683 .await;
684 }
685
686 result
687 })
688 .detach_and_log_err(cx)
689 }
690
691 /// Updates the set of installed extensions.
692 ///
693 /// First, this unloads any themes, languages, or grammars that are
694 /// no longer in the manifest, or whose files have changed on disk.
695 /// Then it loads any themes, languages, or grammars that are newly
696 /// added to the manifest, or whose files have changed on disk.
697 fn extensions_updated(
698 &mut self,
699 new_index: ExtensionIndex,
700 cx: &mut ModelContext<Self>,
701 ) -> Task<()> {
702 let old_index = &self.extension_index;
703
704 // Determine which extensions need to be loaded and unloaded, based
705 // on the changes to the manifest and the extensions that we know have been
706 // modified.
707 let mut extensions_to_unload = Vec::default();
708 let mut extensions_to_load = Vec::default();
709 {
710 let mut old_keys = old_index.extensions.iter().peekable();
711 let mut new_keys = new_index.extensions.iter().peekable();
712 loop {
713 match (old_keys.peek(), new_keys.peek()) {
714 (None, None) => break,
715 (None, Some(_)) => {
716 extensions_to_load.push(new_keys.next().unwrap().0.clone());
717 }
718 (Some(_), None) => {
719 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
720 }
721 (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(&new_key) {
722 Ordering::Equal => {
723 let (old_key, old_value) = old_keys.next().unwrap();
724 let (new_key, new_value) = new_keys.next().unwrap();
725 if old_value != new_value || self.modified_extensions.contains(old_key)
726 {
727 extensions_to_unload.push(old_key.clone());
728 extensions_to_load.push(new_key.clone());
729 }
730 }
731 Ordering::Less => {
732 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
733 }
734 Ordering::Greater => {
735 extensions_to_load.push(new_keys.next().unwrap().0.clone());
736 }
737 },
738 }
739 }
740 self.modified_extensions.clear();
741 }
742
743 if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
744 return Task::ready(());
745 }
746
747 let reload_count = extensions_to_unload
748 .iter()
749 .filter(|id| extensions_to_load.contains(id))
750 .count();
751
752 log::info!(
753 "extensions updated. loading {}, reloading {}, unloading {}",
754 extensions_to_load.len() - reload_count,
755 reload_count,
756 extensions_to_unload.len() - reload_count
757 );
758
759 let themes_to_remove = old_index
760 .themes
761 .iter()
762 .filter_map(|(name, entry)| {
763 if extensions_to_unload.contains(&entry.extension) {
764 Some(name.clone().into())
765 } else {
766 None
767 }
768 })
769 .collect::<Vec<_>>();
770 let languages_to_remove = old_index
771 .languages
772 .iter()
773 .filter_map(|(name, entry)| {
774 if extensions_to_unload.contains(&entry.extension) {
775 Some(name.clone())
776 } else {
777 None
778 }
779 })
780 .collect::<Vec<_>>();
781 let mut grammars_to_remove = Vec::new();
782 for extension_id in &extensions_to_unload {
783 let Some(extension) = old_index.extensions.get(extension_id) else {
784 continue;
785 };
786 grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
787 for (language_server_name, config) in extension.manifest.language_servers.iter() {
788 self.language_registry
789 .remove_lsp_adapter(config.language.as_ref(), language_server_name);
790 }
791 }
792
793 self.wasm_extensions
794 .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
795 self.theme_registry.remove_user_themes(&themes_to_remove);
796 self.language_registry
797 .remove_languages(&languages_to_remove, &grammars_to_remove);
798
799 let languages_to_add = new_index
800 .languages
801 .iter()
802 .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
803 .collect::<Vec<_>>();
804 let mut grammars_to_add = Vec::new();
805 let mut themes_to_add = Vec::new();
806 for extension_id in &extensions_to_load {
807 let Some(extension) = new_index.extensions.get(extension_id) else {
808 continue;
809 };
810
811 grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
812 let mut grammar_path = self.installed_dir.clone();
813 grammar_path.extend([extension_id.as_ref(), "grammars"]);
814 grammar_path.push(grammar_name.as_ref());
815 grammar_path.set_extension("wasm");
816 (grammar_name.clone(), grammar_path)
817 }));
818 themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
819 let mut path = self.installed_dir.clone();
820 path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
821 path
822 }));
823 }
824
825 self.language_registry
826 .register_wasm_grammars(grammars_to_add);
827
828 for (language_name, language) in languages_to_add {
829 let mut language_path = self.installed_dir.clone();
830 language_path.extend([
831 Path::new(language.extension.as_ref()),
832 language.path.as_path(),
833 ]);
834 self.language_registry.register_language(
835 language_name.clone(),
836 language.grammar.clone(),
837 language.matcher.clone(),
838 None,
839 move || {
840 let config = std::fs::read_to_string(language_path.join("config.toml"))?;
841 let config: LanguageConfig = ::toml::from_str(&config)?;
842 let queries = load_plugin_queries(&language_path);
843 Ok((config, queries))
844 },
845 );
846 }
847
848 let fs = self.fs.clone();
849 let wasm_host = self.wasm_host.clone();
850 let root_dir = self.installed_dir.clone();
851 let theme_registry = self.theme_registry.clone();
852 let extension_entries = extensions_to_load
853 .iter()
854 .filter_map(|name| new_index.extensions.get(name).cloned())
855 .collect::<Vec<_>>();
856
857 self.extension_index = new_index;
858 cx.notify();
859 cx.emit(Event::ExtensionsUpdated);
860
861 cx.spawn(|this, mut cx| async move {
862 cx.background_executor()
863 .spawn({
864 let fs = fs.clone();
865 async move {
866 for theme_path in &themes_to_add {
867 theme_registry
868 .load_user_theme(&theme_path, fs.clone())
869 .await
870 .log_err();
871 }
872 }
873 })
874 .await;
875
876 let mut wasm_extensions = Vec::new();
877 for extension in extension_entries {
878 if extension.manifest.lib.kind.is_none() {
879 continue;
880 };
881
882 let mut path = root_dir.clone();
883 path.extend([extension.manifest.id.as_ref(), "extension.wasm"]);
884 let Some(mut wasm_file) = fs
885 .open_sync(&path)
886 .await
887 .context("failed to open wasm file")
888 .log_err()
889 else {
890 continue;
891 };
892
893 let mut wasm_bytes = Vec::new();
894 if wasm_file
895 .read_to_end(&mut wasm_bytes)
896 .context("failed to read wasm")
897 .log_err()
898 .is_none()
899 {
900 continue;
901 }
902
903 let Some(wasm_extension) = wasm_host
904 .load_extension(
905 wasm_bytes,
906 extension.manifest.clone(),
907 cx.background_executor().clone(),
908 )
909 .await
910 .context("failed to load wasm extension")
911 .log_err()
912 else {
913 continue;
914 };
915
916 wasm_extensions.push((extension.manifest.clone(), wasm_extension));
917 }
918
919 this.update(&mut cx, |this, cx| {
920 this.reload_complete_senders.clear();
921
922 for (manifest, wasm_extension) in &wasm_extensions {
923 for (language_server_name, language_server_config) in &manifest.language_servers
924 {
925 this.language_registry.register_lsp_adapter(
926 language_server_config.language.clone(),
927 Arc::new(ExtensionLspAdapter {
928 extension: wasm_extension.clone(),
929 host: this.wasm_host.clone(),
930 config: wit::LanguageServerConfig {
931 name: language_server_name.0.to_string(),
932 language_name: language_server_config.language.to_string(),
933 },
934 }),
935 );
936 }
937 }
938 this.wasm_extensions.extend(wasm_extensions);
939 ThemeSettings::reload_current_theme(cx)
940 })
941 .ok();
942 })
943 }
944
945 fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
946 let fs = self.fs.clone();
947 let work_dir = self.wasm_host.work_dir.clone();
948 let extensions_dir = self.installed_dir.clone();
949 let index_path = self.index_path.clone();
950 cx.background_executor().spawn(async move {
951 let start_time = Instant::now();
952 let mut index = ExtensionIndex::default();
953
954 fs.create_dir(&work_dir).await.log_err();
955 fs.create_dir(&extensions_dir).await.log_err();
956
957 let extension_paths = fs.read_dir(&extensions_dir).await;
958 if let Ok(mut extension_paths) = extension_paths {
959 while let Some(extension_dir) = extension_paths.next().await {
960 let Ok(extension_dir) = extension_dir else {
961 continue;
962 };
963 Self::add_extension_to_index(fs.clone(), extension_dir, &mut index)
964 .await
965 .log_err();
966 }
967 }
968
969 if let Ok(index_json) = serde_json::to_string_pretty(&index) {
970 fs.save(&index_path, &index_json.as_str().into(), Default::default())
971 .await
972 .context("failed to save extension index")
973 .log_err();
974 }
975
976 log::info!("rebuilt extension index in {:?}", start_time.elapsed());
977 index
978 })
979 }
980
981 async fn add_extension_to_index(
982 fs: Arc<dyn Fs>,
983 extension_dir: PathBuf,
984 index: &mut ExtensionIndex,
985 ) -> Result<()> {
986 let mut extension_manifest =
987 Self::load_extension_manifest(fs.clone(), &extension_dir).await?;
988 let extension_id = extension_manifest.id.clone();
989
990 // TODO: distinguish dev extensions more explicitly, by the absence
991 // of a checksum file that we'll create when downloading normal extensions.
992 let is_dev = fs
993 .metadata(&extension_dir)
994 .await?
995 .ok_or_else(|| anyhow!("directory does not exist"))?
996 .is_symlink;
997
998 if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
999 while let Some(language_path) = language_paths.next().await {
1000 let language_path = language_path?;
1001 let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1002 continue;
1003 };
1004 let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1005 continue;
1006 };
1007 if !fs_metadata.is_dir {
1008 continue;
1009 }
1010 let config = fs.load(&language_path.join("config.toml")).await?;
1011 let config = ::toml::from_str::<LanguageConfig>(&config)?;
1012
1013 let relative_path = relative_path.to_path_buf();
1014 if !extension_manifest.languages.contains(&relative_path) {
1015 extension_manifest.languages.push(relative_path.clone());
1016 }
1017
1018 index.languages.insert(
1019 config.name.clone(),
1020 ExtensionIndexLanguageEntry {
1021 extension: extension_id.clone(),
1022 path: relative_path,
1023 matcher: config.matcher,
1024 grammar: config.grammar,
1025 },
1026 );
1027 }
1028 }
1029
1030 if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1031 while let Some(theme_path) = theme_paths.next().await {
1032 let theme_path = theme_path?;
1033 let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1034 continue;
1035 };
1036
1037 let Some(theme_family) = ThemeRegistry::read_user_theme(&theme_path, fs.clone())
1038 .await
1039 .log_err()
1040 else {
1041 continue;
1042 };
1043
1044 let relative_path = relative_path.to_path_buf();
1045 if !extension_manifest.themes.contains(&relative_path) {
1046 extension_manifest.themes.push(relative_path.clone());
1047 }
1048
1049 for theme in theme_family.themes {
1050 index.themes.insert(
1051 theme.name.into(),
1052 ExtensionIndexThemeEntry {
1053 extension: extension_id.clone(),
1054 path: relative_path.clone(),
1055 },
1056 );
1057 }
1058 }
1059 }
1060
1061 let extension_wasm_path = extension_dir.join("extension.wasm");
1062 if fs.is_file(&extension_wasm_path).await {
1063 extension_manifest
1064 .lib
1065 .kind
1066 .get_or_insert(ExtensionLibraryKind::Rust);
1067 }
1068
1069 index.extensions.insert(
1070 extension_id.clone(),
1071 ExtensionIndexEntry {
1072 dev: is_dev,
1073 manifest: Arc::new(extension_manifest),
1074 },
1075 );
1076
1077 Ok(())
1078 }
1079
1080 pub async fn load_extension_manifest(
1081 fs: Arc<dyn Fs>,
1082 extension_dir: &Path,
1083 ) -> Result<ExtensionManifest> {
1084 let extension_name = extension_dir
1085 .file_name()
1086 .and_then(OsStr::to_str)
1087 .ok_or_else(|| anyhow!("invalid extension name"))?;
1088
1089 let mut extension_manifest_path = extension_dir.join("extension.json");
1090 if fs.is_file(&extension_manifest_path).await {
1091 let manifest_content = fs
1092 .load(&extension_manifest_path)
1093 .await
1094 .with_context(|| format!("failed to load {extension_name} extension.json"))?;
1095 let manifest_json = serde_json::from_str::<OldExtensionManifest>(&manifest_content)
1096 .with_context(|| {
1097 format!("invalid extension.json for extension {extension_name}")
1098 })?;
1099
1100 Ok(manifest_from_old_manifest(manifest_json, extension_name))
1101 } else {
1102 extension_manifest_path.set_extension("toml");
1103 let manifest_content = fs
1104 .load(&extension_manifest_path)
1105 .await
1106 .with_context(|| format!("failed to load {extension_name} extension.toml"))?;
1107 toml::from_str(&manifest_content)
1108 .with_context(|| format!("invalid extension.json for extension {extension_name}"))
1109 }
1110 }
1111}
1112
1113fn manifest_from_old_manifest(
1114 manifest_json: OldExtensionManifest,
1115 extension_id: &str,
1116) -> ExtensionManifest {
1117 ExtensionManifest {
1118 id: extension_id.into(),
1119 name: manifest_json.name,
1120 version: manifest_json.version,
1121 description: manifest_json.description,
1122 repository: manifest_json.repository,
1123 authors: manifest_json.authors,
1124 schema_version: 0,
1125 lib: Default::default(),
1126 themes: {
1127 let mut themes = manifest_json.themes.into_values().collect::<Vec<_>>();
1128 themes.sort();
1129 themes.dedup();
1130 themes
1131 },
1132 languages: {
1133 let mut languages = manifest_json.languages.into_values().collect::<Vec<_>>();
1134 languages.sort();
1135 languages.dedup();
1136 languages
1137 },
1138 grammars: manifest_json
1139 .grammars
1140 .into_keys()
1141 .map(|grammar_name| (grammar_name, Default::default()))
1142 .collect(),
1143 language_servers: Default::default(),
1144 }
1145}
1146
1147fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
1148 let mut result = LanguageQueries::default();
1149 if let Some(entries) = std::fs::read_dir(root_path).log_err() {
1150 for entry in entries {
1151 let Some(entry) = entry.log_err() else {
1152 continue;
1153 };
1154 let path = entry.path();
1155 if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
1156 if !remainder.ends_with(".scm") {
1157 continue;
1158 }
1159 for (name, query) in QUERY_FILENAME_PREFIXES {
1160 if remainder.starts_with(name) {
1161 if let Some(contents) = std::fs::read_to_string(&path).log_err() {
1162 match query(&mut result) {
1163 None => *query(&mut result) = Some(contents.into()),
1164 Some(r) => r.to_mut().push_str(contents.as_ref()),
1165 }
1166 }
1167 break;
1168 }
1169 }
1170 }
1171 }
1172 }
1173 result
1174}