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