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